//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: pkcs.cpp // // Contents: Cert Server Extension interfaces -- PKCS implementation // //--------------------------------------------------------------------------- #include #pragma hdrstop #include #include #define SECURITY_WIN32 #include #include "resource.h" #include "cscom.h" #include "csprop.h" #include "elog.h" #include "certlog.h" #include "csdisp.h" #include "cscsp.h" #include #include #include #include "csldap.h" #include "cainfop.h" #define __dwFILE__ __dwFILE_CERTSRV_PKCS_CPP_ CSURLTEMPLATE *g_paRevURL = NULL; DWORD g_caRevURL = 0; CSURLTEMPLATE *g_paCACertURL = NULL; DWORD g_caCACertURL = 0; BSTR g_strDomainDN = NULL; BSTR g_strConfigDN = NULL; WCHAR *g_pwszKRAPublishURL = NULL; WCHAR *g_pwszAIACrossCertPublishURL = NULL; WCHAR *g_pwszRootTrustCrossCertPublishURL = NULL; // Note on Renewal and Key reuse: // // Cert Indexes, Key Indexes and CRL Name Indexes are all zero based. // // One CRL is issued by this CA for each unique key. Each CRL covers all of // the certs issued by this CA for one key, even though the key may have been // used by multiple renewal certs. // // The database IssuerNameID PROPTYPE_LONG column holds the Key Index in the // top 16 bits and the Cert Index in the bottom 16 bits. This allows a pair of // query restrictions to reduce the result row set to those revoked certs // that will be placed into a single CRL. // // When Cert File, Key Container, CRL File or DS Object name templates include // an Index Suffix, an empty string suffix, "", is used when the index is zero. // Otherwise the Suffix is "(%u)", where the index itself is passed to wsprintf // to construct the %u field. // // Cert Indexes increment each time the CA cert is renewed. Because 16 bit // Cert Indexes are stored in the database, a CA may have up to 64k certs, // and be renewed a maximum of 64k-1 times. // // The Cert File Name Suffix is built from the Cert Index. // // Key Indexes: The original installed CA cert uses a Key Index of 0. // If a renewal cert uses the same key as any previous cert used by this CA, // the Key Index for the new CA cert is taken from the previous CA cert. // If a renewal cert uses a new Key, the Cert Index is used for the Key Index. // The primary reason sequential Key Indexes are not used for new keys is that // too much context information is required to determine the next Key Index -- // which is particularly difficult to obtain when performing PFX restore. // // The Key Container Name Suffix is built from the Key Index. // // // CRL Indexes: same as Key Index. // CRL File Name Suffix: same as Key Container Name Suffix. // // Example: Cert CertName Key KeyName CRL CRLName // Index Suffix Index Suffix Index Suffix // Original Install 0 "" 0 "" 0 "" // // Renew, new Key 1 "(1)" 1 "(1)" 1 "(1)" // *Renew, reuse Key 2 "(2)" 1 "(1)" 1 "(1)" // *Renew, reuse Key 3 "(3)" 1 "(1)" 1 "(1)" // // Renew, new Key 4 "(4)" 4 "(4)" 4 "(4)" // *Renew, reuse Key 5 "(5)" 4 "(4)" 4 "(4)" // // Renew, new Key 6 "(6)" 6 "(6)" 6 "(6)" // *Renew, reuse Key 7 "(7)" 6 "(6)" 6 "(6)" // // // CCertRequest::GetCACertificate can be used to fetch Certs and CRLs by Index. // This API always accepts a Cert Index. // // When fetching a certificate: If the Cert Index is valid, the appropriate // certificate or chain is returned, even if it is expired or revoked. // // When fetching a CRL: If the Cert Index is valid AND if the Cert Index // MATCHES the Key Index for the indexed Cert, the appropriate CRL is returned. // This means that an error will be returned when requesting CRLs associated // with entries in the above table that reused keys (marked with an asterisk // in the first column). The nearest previous unmarked entry's CRL covers // revocations for the marked entries. // // // CCertServer{Policy,Exit}::GetCertificateProperty can be used to fetch // information about Certs and CRLs. This API allows an optional numeric // suffix on the property name, as in "RawCRL.3". The suffix is always // interpreted as a Cert Index. // // wszPROPCERTCOUNT: Returns total CA Cert count, including expired and // revoked certs. No numeric Cert Index suffix is allowed. // // wszPROPRAWCACERTIFICATE: Returns the Cert for the passed Cert Index. // Returns the Cert for the most recent Cert Index if no Cert Index is // specified. Expired and revoked certs are still retrievable. // // wszPROPCERTSTATE: Returns the Cert State for the passed Cert Index. // Returns the Cert State for the most recent Cert Index if no Cert Index is // specified. // Values for wszPROPCERTSTATE (see certadm.h): // CA_DISP_REVOKED // This Cert has been revoked. // CA_DISP_VALID // This Cert is still valid // CA_DISP_INVALID // This Cert has expired. // CA_DISP_ERROR // Cert unavailable (placehholder in registry?) // // wszPROPCERTSUFFIX: Returns the Cert FileName Suffix for the passed Cert // Index. Returns the Cert FileName Suffix for the most recent Cert Index if // no Cert Index is specified. // // wszPROPRAWCRL: Returns the CRL for the passed Cert Index. As with // CCertRequest::GetCACertificate, it is an error to fetch a CRL for a Cert // that reused keys. In the above table, only "RawCRL.0", "RawCRL.1", // "RawCRL.4", "RawCRL.6" & "RawCRL" are allowed. "RawCRL" will fetch the most // recent CRL. Use the wszPROPCRLSTATE with a numeric Cert Index suffix to // determine which CRLs are valid to fetch. CA_DISP_ERROR indicates the // CRL cannot be fetched. CA_DISP_REVOKED and CA_DISP_INVALID CRLs are still // retrievable via this method call. // // All of the other CRL-related property fetches are supported for all valid // Cert Index values: // // wszPROPCRLINDEX: Returns the CRL Index value for the passed Cert Index. // Returns the CRL Index value for the most recent Cert Index if no Cert Index // is specified. // // wszPROPCRLSTATE: Returns the CRL State for the passed Cert Index. // Returns the CRL State for the most recent Cert Index if no Cert Index is // specified. // Values for wszPROPCRLSTATE (see certadm.h): // CA_DISP_REVOKED // All unexpired certs using this Cert's CRL have been // // revoked. // CA_DISP_VALID // This Cert is still publishing CRLs as needed. // CA_DISP_INVALID // All certs using this Cert's CRL are expired. // CA_DISP_ERROR // This Cert's CRL is managed by another Cert. // // wszPROPCRLSUFFIX: Returns the CRL FileName Suffix for the passed Cert Index. // Returns the CRL FileName Suffix for the most recent Cert Index if no Cert // Index is specified. CACTX *g_aCAContext; // allocated array of CACTXs CACTX *g_pCAContextCurrent; // current CACTX is last g_aCAContext element CERT_CONTEXT const **g_rgKRACerts = NULL; BSTR *g_rgstrKRAHashes = NULL; DWORD g_cKRAHashes = 0; DWORD g_cKRACertsRoundRobin = 0; DWORD g_iKRACerts; // Next KRA cert to be used by this CA HRESULT g_hrKRALoad = S_OK; DWORD g_cCAKeys; // Total number of unique CA keys managed by this CA DWORD g_cCACerts; // Total number of CA certs managed by this CA DWORD g_cKRACerts; // Total number of KRA certs used by this CA CAXCHGCTX *g_aCAXchgContext; // allocated array of CAXCHGCTXs CAXCHGCTX *g_pCAXchgContextCurrent; // current CAXCHGCTX is last element DWORD g_cCAXchgCerts; // number of CA Xchg certs managed by this CA HCERTSTORE g_hStoreCAXchg = NULL; DWORD g_dwXchgProvType; WCHAR *g_pwszXchgProvName = NULL; ALG_ID g_XchgidAlg; BOOL g_fXchgMachineKeyset; DWORD g_dwXchgKeySize; LONG g_lValidityPeriodCount = dwVALIDITYPERIODCOUNTDEFAULT_STANDALONE; enum ENUM_PERIOD g_enumValidityPeriod = dwVALIDITYPERIODENUMDEFAULT; enum ENUM_PERIOD g_enumCAXchgValidityPeriod = dwCAXCHGVALIDITYPERIODENUMDEFAULT; LONG g_lCAXchgValidityPeriodCount = dwCAXCHGVALIDITYPERIODCOUNTDEFAULT; enum ENUM_PERIOD g_enumCAXchgOverlapPeriod = dwCAXCHGOVERLAPPERIODENUMDEFAULT; LONG g_lCAXchgOverlapPeriodCount = dwCAXCHGOVERLAPPERIODCOUNTDEFAULT; typedef enum { ST_COUNTRY = 0, ST_ORGANIZATION, ST_ORGANIZATIONALUNIT, ST_COMMONNAME, ST_LOCALITY, ST_STATEORPROVINCE, ST_TITLE, ST_GIVENNAME, ST_INITIALS, ST_SURNAME, ST_DOMAINCOMPONENT, ST_EMAIL, ST_STREETADDRESS, ST_UNSTRUCTUREDNAME, ST_UNSTRUCTUREDADDRESS, ST_DEVICESERIALNUMBER, ST_NULL }; typedef struct _SUBJECTTABLE { WCHAR const *pwszPropName; CHAR const *pszObjId; WCHAR const * const *apwszAttributeName; DWORD cchMax; DWORD dwValueType; DWORD dwSubjectTableValue; } SUBJECTTABLE; WCHAR const *apwszAttrCountry[] = { wszATTRCOUNTRY1, wszATTRCOUNTRY2, TEXT(szOID_COUNTRY_NAME), NULL }; WCHAR const *apwszAttrOrg[] = { wszATTRORG1, wszATTRORG2, wszATTRORG3, TEXT(szOID_ORGANIZATION_NAME), NULL }; WCHAR const *apwszAttrOrgUnit[] = { wszATTRORGUNIT1, wszATTRORGUNIT2, wszATTRORGUNIT3, wszATTRORGUNIT4, TEXT(szOID_ORGANIZATIONAL_UNIT_NAME), NULL }; WCHAR const *apwszAttrCommonName[] = { wszATTRCOMMONNAME1, wszATTRCOMMONNAME2, TEXT(szOID_COMMON_NAME), NULL }; WCHAR const *apwszAttrLocality[] = { wszATTRLOCALITY1, wszATTRLOCALITY2, TEXT(szOID_LOCALITY_NAME), NULL }; WCHAR const *apwszAttrState[] = { wszATTRSTATE1, wszATTRSTATE2, wszATTRSTATE3, TEXT(szOID_STATE_OR_PROVINCE_NAME), NULL }; WCHAR const *apwszAttrTitle[] = { wszATTRTITLE1, wszATTRTITLE2, TEXT(szOID_TITLE), NULL }; WCHAR const *apwszAttrGivenName[] = { wszATTRGIVENNAME1, wszATTRGIVENNAME2, TEXT(szOID_GIVEN_NAME), NULL }; WCHAR const *apwszAttrInitials[] = { wszATTRINITIALS1, wszATTRINITIALS2, TEXT(szOID_INITIALS), NULL }; WCHAR const *apwszAttrSurName[] = { wszATTRSURNAME1, wszATTRSURNAME2, TEXT(szOID_SUR_NAME), NULL }; WCHAR const *apwszAttrDomComp[] = { wszATTRDOMAINCOMPONENT1, wszATTRDOMAINCOMPONENT2, TEXT(szOID_DOMAIN_COMPONENT), NULL }; WCHAR const *apwszAttrEMail[] = { wszATTREMAIL1, wszATTREMAIL2, TEXT(szOID_RSA_emailAddr), NULL }; WCHAR const *apwszAttrStreetAddr[] = { wszATTRSTREETADDRESS1, wszATTRSTREETADDRESS2, TEXT(szOID_STREET_ADDRESS), NULL }; WCHAR const *apwszAttrUnstructName[] = { wszATTRUNSTRUCTUREDNAME1, TEXT(szOID_RSA_unstructName), NULL }; WCHAR const *apwszAttrUnstructAddr[] = { wszATTRUNSTRUCTUREDADDRESS1, TEXT(szOID_RSA_unstructAddr), NULL }; WCHAR const *apwszAttrDeviceSerialNumber[] = { wszATTRDEVICESERIALNUMBER1, TEXT(szOID_DEVICE_SERIAL_NUMBER), NULL }; SUBJECTTABLE const pkcs_subject[] = { { // "Country", g_wszPropSubjectCountry, szOID_COUNTRY_NAME, apwszAttrCountry, cchCOUNTRYNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_COUNTRY, }, { // "Organization", g_wszPropSubjectOrganization, szOID_ORGANIZATION_NAME, apwszAttrOrg, cchORGANIZATIONNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_ORGANIZATION, }, { // "OrganizationalUnit", g_wszPropSubjectOrgUnit, szOID_ORGANIZATIONAL_UNIT_NAME, apwszAttrOrgUnit, cchORGANIZATIONALUNITNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_ORGANIZATIONALUNIT, }, { // "CommonName", g_wszPropSubjectCommonName, szOID_COMMON_NAME, apwszAttrCommonName, cchCOMMONNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_COMMONNAME, }, { // "Locality", g_wszPropSubjectLocality, szOID_LOCALITY_NAME, apwszAttrLocality, cchLOCALITYMANAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_LOCALITY, }, { // "StateOrProvince", g_wszPropSubjectState, szOID_STATE_OR_PROVINCE_NAME, apwszAttrState, cchSTATEORPROVINCENAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_STATEORPROVINCE, }, { // "Title", g_wszPropSubjectTitle, szOID_TITLE, apwszAttrTitle, cchTITLEMAX, CERT_RDN_PRINTABLE_STRING, ST_TITLE, }, { // "GivenName", g_wszPropSubjectGivenName, szOID_GIVEN_NAME, apwszAttrGivenName, cchGIVENNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_GIVENNAME, }, { // "Initials", g_wszPropSubjectInitials, szOID_INITIALS, apwszAttrInitials, cchINITIALSMAX, CERT_RDN_PRINTABLE_STRING, ST_INITIALS, }, { // "SurName", g_wszPropSubjectSurName, szOID_SUR_NAME, apwszAttrSurName, cchSURNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_SURNAME, }, { // "DomainComponent", g_wszPropSubjectDomainComponent, szOID_DOMAIN_COMPONENT, apwszAttrDomComp, cchDOMAINCOMPONENTMAX, CERT_RDN_PRINTABLE_STRING, ST_DOMAINCOMPONENT, }, { // "EMail", g_wszPropSubjectEMail, szOID_RSA_emailAddr, apwszAttrEMail, cchEMAILMAX, CERT_RDN_PRINTABLE_STRING, ST_EMAIL, }, { // "StreetAddress", g_wszPropSubjectStreetAddress, szOID_STREET_ADDRESS, apwszAttrStreetAddr, cchSTREETADDRESSMAX, CERT_RDN_PRINTABLE_STRING, ST_STREETADDRESS, }, { // "UnstructuredName", g_wszPropSubjectUnstructuredName, szOID_RSA_unstructName, apwszAttrUnstructName, cchUNSTRUCTUREDNAMEMAX, CERT_RDN_PRINTABLE_STRING, ST_UNSTRUCTUREDNAME, }, { // "UnstructuredAddress", g_wszPropSubjectUnstructuredAddress, szOID_RSA_unstructAddr, apwszAttrUnstructAddr, cchUNSTRUCTUREDADDRESSMAX, CERT_RDN_PRINTABLE_STRING, ST_UNSTRUCTUREDADDRESS, }, { // "DeviceSerialNumber", g_wszPropSubjectDeviceSerialNumber, szOID_DEVICE_SERIAL_NUMBER, apwszAttrDeviceSerialNumber, cchDEVICESERIALNUMBERMAX, CERT_RDN_PRINTABLE_STRING, ST_DEVICESERIALNUMBER, }, { NULL, NULL, NULL, 0, 0, ST_NULL, }, }; #define CSUBJECTTABLE (sizeof(pkcs_subject) / sizeof(pkcs_subject[0])) SUBJECTTABLE const *pkcs_apSubject[CSUBJECTTABLE]; SUBJECTTABLE const **pkcs_ppSubjectLast; BOOL pkcsfSubjectTemplate = FALSE; WCHAR const g_wszCNXchgSuffix[] = wszCNXCHGSUFFIX; WCHAR const g_wszNTAuth[]=L"ldap:///CN=Public Key Services,CN=Services,%s?cACertificate?one?cn=NTAuthCertificates"; #define SHA1_HASH_LENGTH 20 VOID pkcsSetDistinguishedName( IN ICertDBRow *prow, IN DWORD dwTable, IN CERT_NAME_BLOB const *pSubject) { DWORD cwc; WCHAR awcName[CCH_DBMAXTEXT_DN + 1]; HRESULT hr; CSASSERT(PROPTABLE_REQUEST == dwTable || PROPTABLE_CERTIFICATE == dwTable); cwc = CertNameToStr( X509_ASN_ENCODING, const_cast(pSubject), CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, awcName, ARRAYSIZE(awcName)); if (0 != cwc && L'\0' != awcName[0]) { #if DBG_CERTSRV DWORD ReqId; prow->GetRowId(&ReqId); DBGPRINT(( DBG_SS_CERTSRVI, "%ws DN(%u): '%ws'\n", PROPTABLE_REQUEST == dwTable? L"Request" : L"Certificate", ReqId, awcName)); #endif if (wcslen(awcName) == ARRAYSIZE(awcName) - 1) { DBGPRINT(( DBG_SS_CERTSRV, "pkcsSetDistinguishedName: possible DN truncation: %u chars: '%ws'\n", ARRAYSIZE(awcName) - 1, awcName)); } hr = prow->SetProperty( g_wszPropSubjectDistinguishedName, PROPTYPE_STRING | PROPCALLER_SERVER | dwTable, MAXDWORD, (BYTE const *) awcName); _PrintIfError(hr, "SetProperty(DN)"); } } WCHAR const * PKCSMapAttributeName( OPTIONAL IN WCHAR const *pwszAttributeName, OPTIONAL IN CHAR const *pszObjId, OUT DWORD *pdwIndex, OUT DWORD *pcchMax) { SUBJECTTABLE const *pSubjectTable; WCHAR const *pwszPropName = NULL; for (pSubjectTable = pkcs_subject; ; pSubjectTable++) { WCHAR const * const *ppwsz; if (NULL == pSubjectTable->pwszPropName) { goto error; } if (NULL != pwszAttributeName) { for (ppwsz = pSubjectTable->apwszAttributeName; NULL != *ppwsz; ppwsz++) { if (0 == lstrcmpi(pwszAttributeName, *ppwsz)) { break; } } if (NULL != *ppwsz || 0 == lstrcmpi(pwszAttributeName, pSubjectTable->pwszPropName)) { break; } } if (NULL != pszObjId && 0 == strcmp(pszObjId, pSubjectTable->pszObjId)) { break; } } CSASSERT(NULL != pSubjectTable->pwszPropName); pwszPropName = pSubjectTable->pwszPropName; *pdwIndex = pSubjectTable->dwSubjectTableValue; *pcchMax = pSubjectTable->cchMax; error: return(pwszPropName); } HRESULT pkcsFindCAContext( IN DWORD iCert, // MAXDWORD -> use current IN DWORD iKey, // MAXDWORD -> use current OUT CACTX **ppCAContext) { HRESULT hr = E_INVALIDARG; DWORD i; CACTX *pCAContext; *ppCAContext = NULL; // Lookup is either by cert index OR by key index, but not both or neither CSASSERT((MAXDWORD == iCert) ^ (MAXDWORD == iKey)); if (MAXDWORD != iCert) { if ((~_16BITMASK & iCert) || iCert >= g_cCACerts) { _JumpError(hr, error, "bad cert index"); } *ppCAContext = &g_aCAContext[iCert]; CSASSERT(iCert == (*ppCAContext)->iCert); } else { CSASSERT(MAXDWORD != iKey); if ((~_16BITMASK & iKey) || iKey >= g_cCAKeys) { _JumpError(hr, error, "bad key index"); } for (i = g_cCACerts; ; i--) { if (0 == i) { _JumpError(hr, error, "key index not found"); } pCAContext = &g_aCAContext[i - 1]; if (iKey == pCAContext->iKey) { *ppCAContext = pCAContext; break; } } } hr = S_OK; // found it! error: return(hr); } // Returns Cert Index in *piCert on success. // // returned in *piCert: // If iCert input value is not MAXDWORD, validate & return iCert. // If iCert input value is MAXDWORD, return the most current Cert Index. HRESULT PKCSMapCertIndex( IN DWORD iCert, OUT DWORD *piCert, OUT DWORD *pState) { HRESULT hr; CACTX *pCAContext; DBGCODE(DWORD iCertSave = iCert); *pState = CA_DISP_ERROR; if (MAXDWORD == iCert) { iCert = g_cCACerts - 1; } if (iCert >= g_cCACerts) { hr = E_INVALIDARG; _JumpError(hr, error, "bad CertIndex"); } pCAContext = &g_aCAContext[iCert]; PKCSVerifyCAState(pCAContext); *pState = CA_DISP_VALID; if (CTXF_CERTMISSING & pCAContext->Flags) { *pState = CA_DISP_ERROR; } else if (CTXF_REVOKED & pCAContext->Flags) { *pState = CA_DISP_REVOKED; } else if (CTXF_EXPIRED & pCAContext->Flags) { *pState = CA_DISP_INVALID; } *piCert = iCert; hr = S_OK; error: DBGPRINT(( DBG_SS_CERTSRVI, "PKCSMapCertIndex(%u) --> %u, s=%u, hr=%x\n", iCertSave, *piCert, *pState, hr)); return(hr); } // Returns Cert Index in *piCert and CRL Index in *piCRL on success. // // returned in *piCert: // If iCert input value is not MAXDWORD, validate iCert. Look up the newest // Cert Index that uses the same key as the passed iCert. // If iCert input value is MAXDWORD, return the most current Cert Index. // // returned in *piCRL: // CRL index (same as Key Index) // HRESULT PKCSMapCRLIndex( IN DWORD iCert, OUT DWORD *piCert, // returns newest iCert w/matching iKey for passed iCert OUT DWORD *piCRL, OUT DWORD *pState) { HRESULT hr; CACTX *pCAContext; CACTX *pCAContextNewest; DWORD i; DBGCODE(DWORD iCertSave = iCert); hr = PKCSMapCertIndex(iCert, piCert, pState); _JumpIfError(hr, error, "PKCSMapCertIndex"); // Now we know *piCert is a valid Cert Index: pCAContext = &g_aCAContext[*piCert]; *piCRL = pCAContext->iKey; // find the newest iCert with matching iKey for (i = *piCert + 1; i < g_cCACerts; i++) { if (*piCRL == g_aCAContext[i].iKey) { *piCert = i; } } pCAContextNewest = &g_aCAContext[*piCert]; if (CTXF_CRLZOMBIE & pCAContext->Flags) { *pState = CA_DISP_VALID; } else if (pCAContext->iCert != pCAContext->iKey) { *pState = CA_DISP_ERROR; } else if (CTXF_EXPIRED == ((CTXF_EXPIRED | CTXF_SKIPCRL) & pCAContextNewest->Flags)) { *pState = CA_DISP_VALID; } hr = S_OK; error: DBGPRINT(( DBG_SS_CERTSRVI, "PKCSMapCRLIndex(%u) --> %u, iCRL=%u, s=%u, hr=%x\n", iCertSave, *piCert, *piCRL, *pState, hr)); return(hr); } HRESULT PKCSGetCACertStatusCode( IN DWORD iCert, OUT HRESULT *phrCAStatusCode) { HRESULT hr; DWORD State; *phrCAStatusCode = E_FAIL; hr = PKCSMapCertIndex(iCert, &iCert, &State); _JumpIfError(hr, error, "PKCSMapCertIndex"); *phrCAStatusCode = g_aCAContext[iCert].hrVerifyStatus; hr = S_OK; error: return(hr); } HRESULT PKCSGetCAState( IN BOOL fCertState, OUT BYTE *pb) { HRESULT hr; DWORD i; for (i = 0; i < g_cCACerts; i++) { DWORD iCert; DWORD iCRL; DWORD State; if (fCertState) { hr = PKCSMapCertIndex(i, &iCert, &State); _JumpIfError(hr, error, "PKCSMapCertIndex"); } else { hr = PKCSMapCRLIndex(i, &iCert, &iCRL, &State); _JumpIfError(hr, error, "PKCSMapCRLIndex"); } CSASSERT(0 == (~0xff & State)); *pb++ = (BYTE) State; } error: return(hr); } inline DWORD MapHRESULTToKRADisposition(HRESULT hr) { switch(hr) { case CERT_E_EXPIRED: return KRA_DISP_EXPIRED; case CRYPT_E_NOT_FOUND: return KRA_DISP_NOTFOUND; case CRYPT_E_REVOKED: return KRA_DISP_REVOKED; case S_OK: return KRA_DISP_VALID; case CERT_E_UNTRUSTEDROOT: case CERT_E_CHAINING: return KRA_DISP_UNTRUSTED; case ERROR_NOT_FOUND: return KRA_DISP_NOTLOADED; default: return KRA_DISP_INVALID; } } HRESULT PKCSGetKRAState( IN DWORD cKRA, OUT BYTE *pb) { HRESULT hr = S_OK; DWORD dwCount = 0, dwUsedCount; HCERTSTORE hKRAStore = NULL; CERT_CONTEXT const *pCertContext = NULL; hKRAStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE, wszKRA_CERTSTORE); _JumpIfError(hr, error, "CertOpenStore KRA"); for (dwCount = 0, dwUsedCount = 0; dwCount < cKRA; dwCount++) { hr = myFindCACertByHashIndex( hKRAStore, g_wszSanitizedName, CSRH_CAKRACERT, dwCount, NULL, &pCertContext); if (S_OK == hr) { hr = myVerifyKRACertContext( pCertContext, (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags)? CA_VERIFY_FLAGS_IGNORE_OFFLINE : 0); // check if the CA is using this cert (was able to // load it last time it started) if(S_OK==hr) { hr = ERROR_NOT_FOUND; for(dwUsedCount=0; dwUsedCount< g_cKRACerts; dwUsedCount++) { if(CertCompareCertificate( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertContext->pCertInfo, g_rgKRACerts[dwUsedCount]->pCertInfo)) { // the CA is using this KRA cert hr = S_OK; break; } } } } CSASSERT(0 == (~0xff & MapHRESULTToKRADisposition(hr))); pb[dwCount] = (BYTE)MapHRESULTToKRADisposition(hr); hr = S_OK; CertFreeCertificateContext(pCertContext); pCertContext = NULL; } error: if(NULL != pCertContext) { CertFreeCertificateContext(pCertContext); } if (NULL != hKRAStore) { CertCloseStore(hKRAStore, CERT_CLOSE_STORE_CHECK_FLAG); } return hr; } HRESULT pkcsSetRequestNameInfo( IN ICertDBRow *prow, IN CERT_NAME_BLOB const *pSubject, OPTIONAL IN WCHAR const *pwszCNSuffix, IN OUT DWORD *pdwRequestFlags, OUT BOOL *pfSubjectNameSet) { HRESULT hr; CERT_RDN *prdn; CERT_RDN *prdnEnd; CERT_NAME_INFO *pNameInfo = NULL; WCHAR const *pwszPropName; DWORD cbNameInfo; DWORD dwIndex; DWORD cchMax; BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note SUBJECTTABLE const *pSubjectTable; *pfSubjectNameSet = FALSE; ZeroMemory(&afSubjectTable, sizeof(afSubjectTable)); CSASSERT(0 == FALSE); hr = prow->SetProperty( g_wszPropSubjectRawName, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, pSubject->cbData, pSubject->pbData); _JumpIfError(hr, error, "SetProperty"); pkcsSetDistinguishedName(prow, PROPTABLE_REQUEST, pSubject); if (!myDecodeName( X509_ASN_ENCODING, X509_UNICODE_NAME, pSubject->pbData, pSubject->cbData, CERTLIB_USE_LOCALALLOC, &pNameInfo, &cbNameInfo)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeName"); } if (ENUM_TELETEX_ON == (ENUM_TELETEX_MASK & g_fForceTeletex)) { *pdwRequestFlags |= CR_FLG_FORCETELETEX; } if (ENUM_TELETEX_UTF8 & g_fForceTeletex) { *pdwRequestFlags |= CR_FLG_FORCEUTF8; } for ( prdn = pNameInfo->rgRDN, prdnEnd = &prdn[pNameInfo->cRDN]; prdn < prdnEnd; prdn++) { CERT_RDN_ATTR *prdna; CERT_RDN_ATTR *prdnaEnd; for ( prdna = prdn->rgRDNAttr, prdnaEnd = &prdna[prdn->cRDNAttr]; prdna < prdnaEnd; prdna++) { CSASSERT( prdna->dwValueType == CERT_RDN_PRINTABLE_STRING || prdna->dwValueType == CERT_RDN_UNICODE_STRING || prdna->dwValueType == CERT_RDN_TELETEX_STRING || prdna->dwValueType == CERT_RDN_IA5_STRING || prdna->dwValueType == CERT_RDN_UTF8_STRING); if (NULL == prdna->Value.pbData || sizeof(WCHAR) > prdna->Value.cbData || L'\0' == *(WCHAR *) prdna->Value.pbData) { continue; } if (CERT_RDN_TELETEX_STRING == prdna->dwValueType && ENUM_TELETEX_AUTO == (ENUM_TELETEX_MASK & g_fForceTeletex)) { *pdwRequestFlags |= CR_FLG_FORCETELETEX; } pwszPropName = PKCSMapAttributeName( NULL, prdna->pszObjId, &dwIndex, &cchMax); if (NULL != pwszPropName) { BOOL fCN; // CAPI null-terminates strings CSASSERT( sizeof(WCHAR) * wcslen((WCHAR const *) prdna->Value.pbData) == prdna->Value.cbData); fCN = 0 == strcmp(szOID_COMMON_NAME, prdna->pszObjId); hr = PropSetAttributeProperty( prow, afSubjectTable[dwIndex], // fConcatenateRDNs PROPTABLE_REQUEST, cchMax, fCN? pwszCNSuffix : NULL, pwszPropName, (WCHAR const *) prdna->Value.pbData); if (HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) == hr) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; } _JumpIfError(hr, error, "PropSetAttributeProperty"); afSubjectTable[dwIndex] = TRUE; *pfSubjectNameSet = TRUE; if (fCN) { pwszCNSuffix = NULL; } } } } hr = S_OK; error: if (NULL != pNameInfo) { LocalFree(pNameInfo); } return(hr); } HRESULT PKCSSetRequestFlags( IN ICertDBRow *prow, IN BOOL fSet, IN DWORD dwChange) { HRESULT hr; DWORD dwOld; DWORD dwNew; DWORD cb; cb = sizeof(dwOld); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &dwOld); _JumpIfError(hr, error, "GetProperty"); if (fSet) { dwNew = dwOld | dwChange; } else { dwNew = dwOld & ~dwChange; } if (dwOld != dwNew) { hr = prow->SetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwNew), (BYTE const *) &dwNew); _JumpIfError(hr, error, "SetProperty(RequestFlags)"); } error: return(hr); } HRESULT pkcsSetAttributeProperty( IN ICertDBRow *prow, IN WCHAR const *pwszName, IN WCHAR const *pwszValue, IN DWORD dwTable, IN BYTE afSubjectTable[], OUT BOOL *pfSubjectModified, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf) { HRESULT hr; WCHAR const *pwszPropName; DWORD dwIndex; DWORD cwcMax; BOOL fConcatenateRDNs; *pfSubjectModified = FALSE; if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } // See if the attribute name can be mapped to a standard property. pwszPropName = PKCSMapAttributeName(pwszName, NULL, &dwIndex, &cwcMax); if (NULL != pwszPropName) { fConcatenateRDNs = afSubjectTable[dwIndex]; afSubjectTable[dwIndex] = TRUE; *pfSubjectModified = TRUE; } else { pwszPropName = pwszName; cwcMax = MAXDWORD; fConcatenateRDNs = FALSE; dwTable = PROPTABLE_ATTRIBUTE; if (0 == lstrcmpi(g_wszPropRequesterName, pwszPropName)) { if (NULL == pfEnrollOnBehalfOf) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "NULL pfEnrollOnBehalfOf"); } *pfEnrollOnBehalfOf = TRUE; dwTable = PROPTABLE_REQUEST; } } hr = PropSetAttributeProperty( prow, fConcatenateRDNs, dwTable, cwcMax, NULL, // pwszSuffix pwszPropName, pwszValue); _JumpIfError(hr, error, "PropSetAttributeProperty"); error: return(hr); } // Note on Request Attribute and Subject RDN processing: // // Subject RDN strings and Request Attributes may be set several ways, in // the following order. Subsequent changes overwrite earlier changes, so the // order implies precedence: // // - Subject in the inner PKCS10 (if no PKCS10 subject, then use the Subject // in the PKCS7 renewal cert) // - the next outer PKCS7 or CMC Attributes // - ... // - the most outer PKCS7 or CMC Attributes // - Request Attribute string passed with the request when first submitted // - Policy Module may set subject RDNs in the certificate table // - ICertAdmin::SetAttributes' Request Attribute string (if request pending) // // "PKCS7 or CMC Attributes" means either of the following: // 1) Authenticated Attributes associated with a (non-CMC) PKCS7 signer info. // 2) Tagged Attributes and/or RegInfo Control Attributes in a CMC request. // // None of the secured attributes listed in the registry (which is set to // wszzDEFAULTSIGNEDATTRIBUTES by default) may be set unless the source is // PKCS7 or CMC Attributes. // // The original request attribute string is stored in the RequestAttributes // column in the request table. It is never modified after that. Individual // request attribute values are parsed out of this string (when the request is // submitted and when ICertAdmin::SetAttributes is called) and stored in a // Subject RDN column of the request or certificate table if the attribute // name matches an alias for a Subject RDN, or in a unique row in the // attribute table otherwise. // // Individual Subject RDNs may be specified multiple times (multiple "OU", // "CN", strings). If all of the RDNs were set from the same source, // they must be concatenated, but if some RDNs were specified from one source, // then modified by another source, the previous set of RDNs should be // overwritten by the new ones. If the original Request Attribute string // specified "CN:foo\nOU:ou2\nCN:bar", the two CN strings should be // concatenated. If one or more CN values are also specified later by a // single call to ICertAdmin::SetAttributes, the original CN values should be // overwritten by the new value(s). // // It is possible to have the CN strings specified by one source and the OU // strings specified by another. // // Before the policy module gets control, all Subject RDN changes are written // to the Request table. Just before dispatching to the policy module, the // Request Subject RDNs are copied to the Certificate Table RDNs. The policy // module may modify the Certificate Table RDNs only. // // If the request is made pending, ICertAdmin::SetAttributes may be used to // modify request attributes and Certificate Table RDNs. // // The certificate Subject is constructed from the Certificate Table RDNs. HRESULT PKCSParseAttributes( IN ICertDBRow *prow, IN WCHAR const *pwszAttributes, IN BOOL fRegInfo, IN DWORD dwRDNTable, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf) { HRESULT hr; WCHAR *pwszDup = NULL; WCHAR *pwszBuf; WCHAR const *pwszName; WCHAR const *pwszValue; WCHAR const *pwszSecuredAttr; BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note WCHAR *pwszNameAlloc = NULL; WCHAR *pwszValueAlloc = NULL; BOOL fSubjectModified = FALSE; if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } if (NULL == pwszAttributes) { hr = S_OK; goto error; // silently ignore empty string } hr = myDupString(pwszAttributes, &pwszDup); _JumpIfError(hr, error, "myDupString"); pwszBuf = pwszDup; ZeroMemory(&afSubjectTable, sizeof(afSubjectTable)); CSASSERT(0 == FALSE); while (TRUE) { hr = myParseNextAttribute(&pwszBuf, fRegInfo, &pwszName, &pwszValue); if (S_FALSE == hr) { break; } _JumpIfError(hr, error, "myParseNextAttribute"); if (fRegInfo) { if (NULL != pwszNameAlloc) { LocalFree(pwszNameAlloc); pwszNameAlloc = NULL; } if (NULL != pwszValueAlloc) { LocalFree(pwszValueAlloc); pwszValueAlloc = NULL; } hr = myUncanonicalizeURLParm(pwszName, &pwszNameAlloc); _JumpIfError(hr, error, "myUncanonicalizeURLParm"); hr = myUncanonicalizeURLParm(pwszValue, &pwszValueAlloc); _JumpIfError(hr, error, "myUncanonicalizeURLParm"); pwszName = pwszNameAlloc; pwszValue = pwszValueAlloc; } if (!fRegInfo) { // Only set the attribute if it's not one of the attributes // that is required to be secure. for (pwszSecuredAttr = g_wszzSecuredAttributes; NULL != pwszSecuredAttr && L'\0' != *pwszSecuredAttr; pwszSecuredAttr += wcslen(pwszSecuredAttr) + 1) { if (0 == lstrcmpi(pwszSecuredAttr, pwszName)) { break; } } } if (fRegInfo || NULL == pwszSecuredAttr || L'\0' == *pwszSecuredAttr) { BOOL fEnrollOnBehalfOf = FALSE; BOOL fSubjectModifiedT = FALSE; hr = pkcsSetAttributeProperty( prow, pwszName, pwszValue, dwRDNTable, afSubjectTable, &fSubjectModified, NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL); _JumpIfError(hr, error, "PKCSSetRequestFlags"); if (fSubjectModifiedT) { fSubjectModified = TRUE; } if (fEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = TRUE; } } } if (fSubjectModified) { hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED); _JumpIfError(hr, error, "PKCSSetRequestFlags"); } hr = S_OK; error: if (NULL != pwszNameAlloc) { LocalFree(pwszNameAlloc); } if (NULL != pwszValueAlloc) { LocalFree(pwszValueAlloc); } if (NULL != pwszDup) { LocalFree(pwszDup); } return(hr); } HRESULT pkcsSetAltSubjectNameExtension( IN ICertDBRow *prow, IN DWORD ExtFlags, IN CERT_EXTENSION const *rgExtension, IN DWORD cExtension, IN DWORD cAltSubjectExtension) { HRESULT hr = S_OK; CERT_ALT_NAME_INFO **apInfo = NULL; DWORD i; DWORD j; DWORD cInfo = 0; DWORD cb; CERT_ALT_NAME_INFO ResultInfo; DWORD cbResult; BYTE *pbResult = NULL; ResultInfo.cAltEntry = 0; ResultInfo.rgAltEntry = NULL; apInfo = (CERT_ALT_NAME_INFO **) LocalAlloc( LMEM_FIXED, sizeof(CERT_ALT_NAME_INFO *) * cAltSubjectExtension); if (NULL == apInfo) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } // Decode all AltNames for (i = 0; i < cExtension; i++) { // This is an OID, generated by capi2, so we don't need to // do a case-insensitive comparison if (0 == strcmp(rgExtension[i].pszObjId, szOID_SUBJECT_ALT_NAME2)) { CSASSERT(cInfo < cAltSubjectExtension); // Decode to plain text if (!myDecodeObject( X509_ASN_ENCODING, X509_ALTERNATE_NAME, rgExtension[i].Value.pbData, rgExtension[i].Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &apInfo[cInfo], &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } if (rgExtension[i].fCritical) { ExtFlags |= EXTENSION_CRITICAL_FLAG; } ResultInfo.cAltEntry += apInfo[cInfo]->cAltEntry; cInfo++; } } CSASSERT(cInfo == cAltSubjectExtension); ResultInfo.rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc( LMEM_FIXED, ResultInfo.cAltEntry * sizeof(CERT_ALT_NAME_ENTRY)); if (NULL == ResultInfo.rgAltEntry) { hr = E_OUTOFMEMORY; _JumpIfError(hr, error, "LocalAlloc"); } j = 0; for (i = 0; i < cInfo; i++) { CopyMemory( &ResultInfo.rgAltEntry[j], apInfo[i]->rgAltEntry, apInfo[i]->cAltEntry * sizeof(CERT_ALT_NAME_ENTRY)); j += apInfo[i]->cAltEntry; } if (!myEncodeObject( X509_ASN_ENCODING, X509_ALTERNATE_NAME, &ResultInfo, 0, CERTLIB_USE_LOCALALLOC, &pbResult, &cbResult)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_REQUEST, TEXT(szOID_SUBJECT_ALT_NAME2), ExtFlags, cbResult, pbResult); _JumpIfError(hr, error, "PropSetExtension"); error: if (NULL != apInfo) { for (i = 0; i < cInfo; i++) { if (NULL != apInfo[i]) { LocalFree(apInfo[i]); } } LocalFree(apInfo); } if (NULL != ResultInfo.rgAltEntry) { LocalFree(ResultInfo.rgAltEntry); } if (NULL != pbResult) { LocalFree(pbResult); } return(hr); } // Scan extension array, and merge all the AltSubjectName Extensions into one. HRESULT pkcsSetExtensions( IN ICertDBRow *prow, IN DWORD ExtFlags, IN CERT_EXTENSION const *rgExtension, IN DWORD cExtension) { HRESULT hr; WCHAR *pwszObjId = NULL; CERT_EXTENSION const *pExt; CERT_EXTENSION const *pExtEnd; DWORD cAltSubjectExtension = 0; pExtEnd = &rgExtension[cExtension]; for (pExt = rgExtension; pExt < pExtEnd; pExt++) { DWORD ExtFlagsT; if (EXTENSION_ORIGIN_RENEWALCERT == (EXTENSION_ORIGIN_MASK & ExtFlags)) { char const * const *ppszObjId; static char const * const apszObjIdFilter[] = { szOID_CERTSRV_CA_VERSION, szOID_AUTHORITY_INFO_ACCESS, szOID_CRL_DIST_POINTS, szOID_AUTHORITY_KEY_IDENTIFIER2, szOID_SUBJECT_KEY_IDENTIFIER, NULL }; for (ppszObjId = apszObjIdFilter; NULL != *ppszObjId; ppszObjId++) { if (0 == strcmp(*ppszObjId, pExt->pszObjId)) { break; } } if (NULL != *ppszObjId) { continue; // skip this extension } } if (NULL != pwszObjId) { LocalFree(pwszObjId); pwszObjId = NULL; } if (!ConvertSzToWsz(&pwszObjId, pExt->pszObjId, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertSzToWsz(ObjId)"); } ExtFlagsT = ExtFlags; if (pExt->fCritical) { ExtFlagsT |= EXTENSION_CRITICAL_FLAG; } // AltSubjectName needs to be merged, so we do that later. // This is an OID, generated by capi2, so we don't need to // do a case-insensitive comparison. if (0 == lstrcmp(pwszObjId, TEXT(szOID_SUBJECT_ALT_NAME2))) { cAltSubjectExtension++; continue; } hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_REQUEST, pwszObjId, ExtFlagsT, pExt->Value.cbData, pExt->Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); DBGPRINT(( DBG_SS_CERTSRVI, "PropSetExtension(%ws, f=%x, cb=%x, pb=%x)\n", pwszObjId, ExtFlagsT, pExt->Value.cbData, pExt->Value.pbData)); } if (0 != cAltSubjectExtension) { hr = pkcsSetAltSubjectNameExtension( prow, ExtFlags, rgExtension, cExtension, cAltSubjectExtension); _JumpIfError(hr, error, "pkcsSetAltSubjectNameExtension"); } hr = S_OK; error: if (NULL != pwszObjId) { LocalFree(pwszObjId); } return(hr); } HRESULT pkcsSetOSVersion( IN ICertDBRow *prow, IN CRYPT_ATTR_BLOB *pAttrBlob) { HRESULT hr; CERT_NAME_VALUE *pOSVersionString = NULL; BSTR strVersion = NULL; DWORD cb; if (!myDecodeObject( X509_ASN_ENCODING, X509_ANY_STRING, pAttrBlob->pbData, pAttrBlob->cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pOSVersionString, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } if (NULL != pOSVersionString) { if (!IS_CERT_RDN_CHAR_STRING(pOSVersionString->dwValueType)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "version string is numeric data"); } // If it's an 8 bit string, convert it to UNICODE if (CERT_RDN_UNIVERSAL_STRING > pOSVersionString->dwValueType) { // Pass byte count in to allocate enough characters for // the converted Unicode string strVersion = SysAllocStringLen( NULL, pOSVersionString->Value.cbData); // This is expected to be only numbers and '.'s, if (NULL == strVersion) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "SysAllocStringLen"); } mbstowcs( strVersion, (char const *) pOSVersionString->Value.pbData, pOSVersionString->Value.cbData); } else if (CERT_RDN_BMP_STRING == pOSVersionString->dwValueType || CERT_RDN_UNICODE_STRING == pOSVersionString->dwValueType) { strVersion = SysAllocStringLen( (WCHAR *) pOSVersionString->Value.pbData, pOSVersionString->Value.cbData/sizeof(WCHAR)); if (NULL == strVersion) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "SysAllocStringLen"); } } else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "unknown string type"); } hr = prow->SetProperty( g_wszPropRequestOSVersion, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, SysStringByteLen(strVersion), (BYTE *) strVersion); _JumpIfError(hr, error, "SetProperty"); } hr = S_OK; error: if (NULL != strVersion) { SysFreeString(strVersion); } if (NULL != pOSVersionString) { LocalFree(pOSVersionString); } return(hr); } HRESULT pkcsSetCSPProvider( IN ICertDBRow *prow, IN CRYPT_ATTR_BLOB *pAttrBlob) { HRESULT hr; CRYPT_CSP_PROVIDER *pccp = NULL; hr = myDecodeCSPProviderAttribute( pAttrBlob->pbData, pAttrBlob->cbData, &pccp); _JumpIfError(hr, error, "myDecodeCSPProviderAttribute"); if (NULL != pccp->pwszProviderName && L'\0' != *pccp->pwszProviderName) { hr = prow->SetProperty( g_wszPropRequestCSPProvider, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, MAXDWORD, (BYTE const *) pccp->pwszProviderName); _JumpIfError(hr, error, "SetProperty"); } hr = S_OK; error: if (NULL != pccp) { LocalFree(pccp); } return(hr); } HRESULT pkcsSetExtensionsFromAttributeBlob( IN ICertDBRow *prow, IN DWORD ExtFlags, IN CRYPT_ATTRIBUTE const *pAttrib) { HRESULT hr; CRYPT_ATTR_BLOB *pAttrBlob; CERT_NAME_VALUE *pNameInfo = NULL; CERT_EXTENSIONS *pCertExtensions = NULL; DWORD cb; pAttrBlob = pAttrib->rgValue; while (TRUE) { if (NULL != pCertExtensions) { LocalFree(pCertExtensions); pCertExtensions = NULL; } if (myDecodeObject( X509_ASN_ENCODING, X509_EXTENSIONS, pAttrBlob->pbData, pAttrBlob->cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pCertExtensions, &cb)) { break; // success } hr = myHLastError(); // if we already decoded the attribute as a T61 string, or if it is // not a PKCS 9.14 attribute, fail -- we don't know what it contains. if (NULL != pNameInfo || 0 != strcmp(pAttrib->pszObjId, szOID_RSA_certExtensions)) { _JumpError(hr, error, "myDecodeObject"); } // Decode the attribute as a T61 string. Some implementations wrap the // PKCS 9.14 extension array in an extra level of encoding as a Teletex // string. if (!myDecodeObject( X509_ASN_ENCODING, X509_ANY_STRING, pAttrBlob->pbData, pAttrBlob->cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pNameInfo, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } // Loop again and try to decode the raw name blob as X509_EXTENSIONS. pAttrBlob = &pNameInfo->Value; } hr = pkcsSetExtensions( prow, EXTENSION_DISABLE_FLAG | ExtFlags, pCertExtensions->rgExtension, pCertExtensions->cExtension); _JumpIfError(hr, error, "pkcsSetExtensions(attributes)"); error: if (NULL != pNameInfo) { LocalFree(pNameInfo); } if (NULL != pCertExtensions) { LocalFree(pCertExtensions); } return(hr); } HRESULT PKCSGetProperty( IN ICertDBRow *prow, IN WCHAR const *pwszPropName, IN DWORD Flags, OPTIONAL OUT DWORD *pcbData, OUT BYTE **ppbData) { HRESULT hr; BYTE *pbData = NULL; DWORD cbData; if (NULL != pcbData) { *pcbData = 0; } *ppbData = NULL; cbData = 0; hr = prow->GetProperty(pwszPropName, Flags, &cbData, pbData); _JumpIfError2(hr, error, "GetProperty", CERTSRV_E_PROPERTY_EMPTY); pbData = (BYTE *) LocalAlloc(LMEM_FIXED, cbData); if (NULL == pbData) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = prow->GetProperty(pwszPropName, Flags, &cbData, pbData); _JumpIfError(hr, error, "GetProperty"); if (NULL != pcbData) { *pcbData = cbData; } *ppbData = pbData; pbData = NULL; error: if (NULL != pbData) { LocalFree(pbData); } return(hr); } VOID pkcsFreePublicKeyInfo( IN OUT CERT_PUBLIC_KEY_INFO *pPublicKeyInfo) { if (NULL != pPublicKeyInfo->Algorithm.pszObjId) { LocalFree(pPublicKeyInfo->Algorithm.pszObjId); } if (NULL != pPublicKeyInfo->Algorithm.Parameters.pbData) { LocalFree(pPublicKeyInfo->Algorithm.Parameters.pbData); } if (NULL != pPublicKeyInfo->PublicKey.pbData) { LocalFree(pPublicKeyInfo->PublicKey.pbData); } ZeroMemory(pPublicKeyInfo, sizeof(*pPublicKeyInfo)); } HRESULT pkcsGetPublicKeyInfo( IN ICertDBRow *prow, OUT CERT_PUBLIC_KEY_INFO *pPublicKeyInfo) { HRESULT hr; WCHAR *pwszObjId = NULL; ZeroMemory(pPublicKeyInfo, sizeof(*pPublicKeyInfo)); hr = PKCSGetProperty( prow, g_wszPropCertificatePublicKeyAlgorithm, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, NULL, (BYTE **) &pwszObjId); _JumpIfError(hr, error, "PKCSGetProperty"); if (!ConvertWszToSz(&pPublicKeyInfo->Algorithm.pszObjId, pwszObjId, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz(AlgObjId)"); } hr = PKCSGetProperty( prow, g_wszPropCertificateRawPublicKeyAlgorithmParameters, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &pPublicKeyInfo->Algorithm.Parameters.cbData, &pPublicKeyInfo->Algorithm.Parameters.pbData); if (CERTSRV_E_PROPERTY_EMPTY != hr) { _JumpIfError(hr, error, "PKCSGetProperty"); } hr = PKCSGetProperty( prow, g_wszPropCertificateRawPublicKey, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &pPublicKeyInfo->PublicKey.cbData, &pPublicKeyInfo->PublicKey.pbData); _JumpIfError(hr, error, "PKCSGetProperty"); error: if (S_OK != hr) { pkcsFreePublicKeyInfo(pPublicKeyInfo); } if (NULL != pwszObjId) { LocalFree(pwszObjId); } return(hr); } HRESULT pkcsEncryptPrivateKey( IN BYTE *pbDecrypted, IN DWORD cbDecrypted, OUT BYTE **ppbEncrypted, OUT DWORD *pcbEncrypted, OUT WCHAR **ppwszKRAHashes) { HRESULT hr; DWORD i; DWORD iKRACert; DWORD cwc; WCHAR *pwszKRAHashes = NULL; CERT_CONTEXT const **rgKRACerts = NULL; static bool fUseCAProv = true; *ppbEncrypted = NULL; *ppwszKRAHashes = NULL; CSASSERT( NULL != g_rgKRACerts && 0 != g_cKRACerts && 0 != g_cKRACertsRoundRobin && NULL != g_rgstrKRAHashes); for (cwc = 0, i = 0; i < g_cKRACertsRoundRobin; i++) { iKRACert = (g_iKRACerts + i) % g_cKRACerts; cwc += wcslen(g_rgstrKRAHashes[iKRACert]) + 1; } pwszKRAHashes = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == pwszKRAHashes) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } rgKRACerts = (CERT_CONTEXT const **) LocalAlloc( LMEM_FIXED, g_cKRACertsRoundRobin * sizeof(rgKRACerts[0])); if (NULL == rgKRACerts) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwszKRAHashes[0] = L'\0'; for (i = 0; i < g_cKRACertsRoundRobin; i++) { iKRACert = (g_iKRACerts + i) % g_cKRACerts; rgKRACerts[i] = g_rgKRACerts[iKRACert]; if (0 != i) { wcscat(pwszKRAHashes, L"\n"); } wcscat(pwszKRAHashes, g_rgstrKRAHashes[iKRACert]); } CSASSERT(wcslen(pwszKRAHashes) + 1 == cwc); hr = myCryptEncryptMessage( CALG_3DES, g_cKRACertsRoundRobin, // cCertRecipient rgKRACerts, // rgCertRecipient pbDecrypted, cbDecrypted, fUseCAProv? g_pCAContextCurrent->hProvCA : NULL, ppbEncrypted, pcbEncrypted); if (FAILED(hr) && fUseCAProv) { // Failed to use the CA HCRYPTPROV, fall back to // default fUseCAProv = false; hr = myCryptEncryptMessage( CALG_3DES, g_cKRACertsRoundRobin, // cCertRecipient rgKRACerts, // rgCertRecipient pbDecrypted, cbDecrypted, NULL, ppbEncrypted, pcbEncrypted); } _JumpIfError(hr, error, "myCryptEncryptMessage"); *ppwszKRAHashes = pwszKRAHashes; pwszKRAHashes = NULL; error: if (NULL != pwszKRAHashes) { LocalFree(pwszKRAHashes); } if (NULL != rgKRACerts) { LocalFree(rgKRACerts); } return(hr); } HRESULT PKCSArchivePrivateKey( IN ICertDBRow *prow, IN BOOL fV1Cert, IN BOOL fOverwrite, IN CRYPT_ATTR_BLOB const *pBlobEncrypted, OPTIONAL IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; BYTE *pbDecrypted = NULL; DWORD cbDecrypted; BYTE *pbEncrypted = NULL; DWORD cbEncrypted; DWORD iCertSig; BYTE *pbCert; // do not free! DWORD cbCert; WCHAR *pwszKRAHashes = NULL; CERT_PUBLIC_KEY_INFO PublicKeyInfo; WCHAR *pwszUserName = NULL; DWORD cb; BYTE *pbKeyHash = NULL; DWORD cbKeyHash; ZeroMemory(&PublicKeyInfo, sizeof(PublicKeyInfo)); if (0 == g_cKRACerts) { if (0 == g_cKRAHashes) { hr = CERTSRV_E_KEY_ARCHIVAL_NOT_CONFIGURED; } else { hr = CERTSRV_E_NO_VALID_KRA; } _JumpError(hr, error, "no KRA encryption certs"); } if (NULL != pResult) { if (NULL == pResult->pbKeyHashIn) { if (0 == (CRLF_ACCEPT_OLDRFC_CMC & g_dwCRLFlags)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "missing encrypted key hash"); } } else { hr = myCalculateKeyArchivalHash( pBlobEncrypted->pbData, pBlobEncrypted->cbData, &pbKeyHash, &cbKeyHash); _JumpIfError(hr, error, "myCalculateKeyArchivalHash"); if (pResult->cbKeyHashIn != cbKeyHash || 0 != memcmp(pResult->pbKeyHashIn, pbKeyHash, cbKeyHash)) { hr = S_OK; _JumpError(S_FALSE, error, "Ignoring key: hash mismatch"); } } } hr = prow->GetProperty( g_wszPropRequestRawArchivedKey, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, NULL); if (CERTSRV_E_PROPERTY_EMPTY != hr) { if (S_OK == hr && !fOverwrite) { hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS); } _JumpIfError2( hr, error, "GetProperty", HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS)); } hr = PKCSGetProperty( prow, g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, NULL, (BYTE **) &pwszUserName); _JumpIfError(hr, error, "PKCSGetProperty"); if (NULL == g_hStoreCAXchg) { hr = PKCSGetCAXchgCert(0, pwszUserName, &iCertSig, &pbCert, &cbCert); _JumpIfError(hr, error, "PKCSGetCAXchgCert"); } CSASSERT(NULL != g_hStoreCAXchg); hr = myCryptDecryptMessage( g_hStoreCAXchg, pBlobEncrypted->pbData, pBlobEncrypted->cbData, CERTLIB_USE_LOCALALLOC, &pbDecrypted, &cbDecrypted); _JumpIfError(hr, error, "myCryptDecryptMessage"); DBGDUMPHEX((DBG_SS_CERTSRVI, 0, pbDecrypted, cbDecrypted)); hr = pkcsGetPublicKeyInfo(prow, &PublicKeyInfo); _JumpIfError(hr, error, "pkcsGetPublicKeyInfo"); hr = myValidateKeyBlob( pbDecrypted, cbDecrypted, &PublicKeyInfo, fV1Cert, NULL); _JumpIfError(hr, error, "myValidateKeyBlob"); hr = pkcsEncryptPrivateKey( pbDecrypted, cbDecrypted, &pbEncrypted, &cbEncrypted, &pwszKRAHashes); _JumpIfError(hr, error, "pkcsEncryptPrivateKey"); hr = prow->SetProperty( g_wszPropRequestRawArchivedKey, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, cbEncrypted, pbEncrypted); _JumpIfError(hr, error, "PKCSArchivePrivateKey:SetProperty"); hr = prow->SetProperty( g_wszPropRequestKeyRecoveryHashes, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszKRAHashes); _JumpIfError(hr, error, "SetProperty"); if (NULL != pResult && NULL == pResult->pbKeyHashOut) { pResult->pbKeyHashOut = pbKeyHash; pResult->cbKeyHashOut = cbKeyHash; pbKeyHash = NULL; } { CertSrv::CAuditEvent audit(SE_AUDITID_CERTSRV_KEYARCHIVED, g_dwAuditFilter); DWORD dwRequestID = 0; DWORD cb = sizeof(DWORD); if (audit.IsEventEnabled()) { hr = prow->GetProperty( g_wszPropRequestRequestID, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *)&dwRequestID); _JumpIfError(hr, error, "Report"); hr = audit.AddData(dwRequestID); // %1 request ID _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszUserName); // %2 requester _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszKRAHashes);// %3 KRA hashes _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.Report(); _JumpIfError(hr, error, "Report"); } } error: pkcsFreePublicKeyInfo(&PublicKeyInfo); if (NULL != pbDecrypted) { ZeroMemory(pbDecrypted, cbDecrypted); // Private Key Material! LocalFree(pbDecrypted); } if (NULL != pbEncrypted) { LocalFree(pbEncrypted); } if (NULL != pwszKRAHashes) { LocalFree(pwszKRAHashes); } if (NULL != pwszUserName) { LocalFree(pwszUserName); } if (NULL != pbKeyHash) { LocalFree(pbKeyHash); } return(hr); } HRESULT pkcsSaveRequestWithoutArchivedKey( IN ICertDBRow *prow, IN DWORD cbIn, IN BYTE const *pbIn) { HRESULT hr; HCRYPTMSG hMsg = NULL; DWORD cSigner; DWORD iSigner; DWORD i; DWORD cb; BYTE *pbWithoutKey = NULL; DWORD cbWithoutKey; CRYPT_ATTRIBUTES *pAttrib = NULL; BOOL fKeyDeleted = FALSE; hMsg = CryptMsgOpenToDecode( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, // dwFlags 0, // dwMsgType NULL, // hCryptProv NULL, // pRecipientInfo NULL); // pStreamInfo if (NULL == hMsg) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgOpenToDecode"); } if (!CryptMsgUpdate(hMsg, pbIn, cbIn, TRUE)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgUpdate"); } cb = sizeof(cSigner); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, &cSigner, &cb)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgGetParam(signer count)"); } DBGPRINT((DBG_SS_CERTSRV, "cSigner=%u\n", cSigner)); for (iSigner = 0; iSigner < cSigner; iSigner++) { hr = myCryptMsgGetParam( hMsg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, iSigner, // dwIndex CERTLIB_USE_LOCALALLOC, (VOID **) &pAttrib, &cb); _PrintIfError2(hr, "myCryptMsgGetParam(content)", hr); if (S_FALSE == hr) { continue; } _JumpIfError(hr, error, "myCryptMsgGetParam(content)"); DBGPRINT(( DBG_SS_CERTSRV, "iSigner=%u, cAttr=%u\n", iSigner, pAttrib->cAttr)); // Loop through deleting attributes from the end to avoid invalidated // indexes, which may result from deleting earlier attributes. for (i = 0; i < pAttrib->cAttr; i++) { CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR_PARA DelPara; DWORD iAttr = pAttrib->cAttr - i - 1; DBGPRINT(( DBG_SS_CERTSRV, "iSigner=%u, iAttr=%u %hs\n", iSigner, iAttr, pAttrib->rgAttr[iAttr].pszObjId)); if (0 == strcmp( pAttrib->rgAttr[iAttr].pszObjId, szOID_ARCHIVED_KEY_ATTR)) { ZeroMemory(&DelPara, sizeof(DelPara)); DelPara.cbSize = sizeof(DelPara); DelPara.dwSignerIndex = iSigner; DelPara.dwUnauthAttrIndex = iAttr; DBGPRINT(( DBG_SS_CERTSRV, "Delete Key(signer=%u, attrib=%u)\n", DelPara.dwSignerIndex, DelPara.dwUnauthAttrIndex)); if (!CryptMsgControl( hMsg, 0, CMSG_CTRL_DEL_SIGNER_UNAUTH_ATTR, &DelPara)) { hr = myHLastError(); _PrintError(hr, "CryptMsgControl"); } fKeyDeleted = TRUE; } } LocalFree(pAttrib); pAttrib = NULL; } if (!fKeyDeleted) { hr = S_FALSE; _JumpError(hr, error, "no Encrypted Key attribute"); } hr = myCryptMsgGetParam( hMsg, CMSG_ENCODED_MESSAGE, 0, // dwIndex CERTLIB_USE_LOCALALLOC, (VOID **) &pbWithoutKey, &cbWithoutKey); _JumpIfError(hr, error, "myCryptMsgGetParam(content)"); hr = prow->SetProperty( g_wszPropRequestRawRequest, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, cbWithoutKey, pbWithoutKey); _JumpIfError(hr, error, "SetProperty(request)"); error: if (NULL != pAttrib) { LocalFree(pAttrib); } if (NULL != pbWithoutKey) { LocalFree(pbWithoutKey); } if (NULL != hMsg) { CryptMsgClose(hMsg); } return(hr); } #define PSA_DISALLOW_EXTENSIONS 0x00000001 #define PSA_DISALLOW_NAMEVALUEPAIRS 0x00000002 #define PSA_DISALLOW_ARCHIVEDKEY 0x00000004 HRESULT pkcsSetAttributes( IN ICertDBRow *prow, IN DWORD ExtFlags, IN DWORD dwDisallowFlags, IN CRYPT_ATTRIBUTE const *rgAttrib, IN DWORD cAttrib, IN DWORD cbRequest, OPTIONAL IN BYTE const *pbRequest, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; CRYPT_ATTRIBUTE const *pAttrib; CRYPT_ATTRIBUTE const *pAttribEnd; DWORD i; BYTE afSubjectTable[CSUBJECTTABLE]; // see PKCSParseAttributes note CRYPT_DATA_BLOB *pBlob = NULL; BOOL fSubjectModified = FALSE; ZeroMemory(&afSubjectTable, sizeof(afSubjectTable)); CSASSERT(0 == FALSE); if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } pAttribEnd = &rgAttrib[cAttrib]; for (pAttrib = rgAttrib; pAttrib < pAttribEnd; pAttrib++) { if (0 == strcmp(pAttrib->pszObjId, szOID_OS_VERSION)) { if (1 != pAttrib->cValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Attribute Value count != 1"); } hr = pkcsSetOSVersion(prow, pAttrib->rgValue); _JumpIfError(hr, error, "pkcsSetOSVersion"); } else if (0 == strcmp(pAttrib->pszObjId, szOID_ENROLLMENT_CSP_PROVIDER)) { // Check to see if we have a CSPPROVIDER attribute. We use this in // the policy module to determine if xenroll generated the request, // so we can behave differently for old xenroll requests (put the // UPN in the subject to avoid enrollment loops) if (1 != pAttrib->cValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Attribute Value count != 1"); } hr = pkcsSetCSPProvider(prow, pAttrib->rgValue); _JumpIfError(hr, error, "pkcsSetCSPProvider"); } else if (0 == strcmp(pAttrib->pszObjId, szOID_ENCRYPTED_KEY_HASH)) { DWORD cb; if (NULL != pResult->pbKeyHashIn || NULL != pBlob) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Multiple key hashes"); } if (1 != pAttrib->cValue) { _JumpError(hr, error, "Attribute Value count != 1"); } if (!myDecodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, pAttrib->rgValue[0].pbData, pAttrib->rgValue[0].cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pBlob, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } pResult->pbKeyHashIn = (BYTE *) LocalAlloc( LMEM_FIXED, pBlob->cbData); if (NULL == pResult->pbKeyHashIn) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(pResult->pbKeyHashIn, pBlob->pbData, pBlob->cbData); pResult->cbKeyHashIn = pBlob->cbData; } else if (0 == strcmp(pAttrib->pszObjId, szOID_ARCHIVED_KEY_ATTR)) { // Pull encrypted private key out of the attribute for archival. // // Save request in database without private key now, to keep the // error path from saving the request later *with* the key. if (NULL != pbRequest) { hr = pkcsSaveRequestWithoutArchivedKey( prow, cbRequest, pbRequest); _PrintIfError(hr, "pkcsSaveRequestWithoutArchivedKey"); if (S_OK == hr) { pResult->fKeyArchived = TRUE; } } hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); if (PSA_DISALLOW_ARCHIVEDKEY & dwDisallowFlags) { hr = CERTSRV_E_BAD_REQUEST_KEY_ARCHIVAL; _JumpError(hr, error, "archived key disallowed"); } if (1 != pAttrib->cValue) { _JumpError(hr, error, "Attribute Value count != 1"); } hr = PKCSArchivePrivateKey( prow, FALSE, FALSE, &pAttrib->rgValue[0], pResult); _JumpIfError(hr, error, "PKCSArchivePrivateKey"); } else if (0 == strcmp(pAttrib->pszObjId, szOID_CERT_EXTENSIONS) || 0 == strcmp(pAttrib->pszObjId, szOID_RSA_certExtensions)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); if (PSA_DISALLOW_EXTENSIONS & dwDisallowFlags) { _JumpError(hr, error, "extensions disallowed"); } if (1 != pAttrib->cValue) { _JumpError(hr, error, "Attribute Value count != 1"); } hr = pkcsSetExtensionsFromAttributeBlob( prow, ExtFlags, pAttrib); _JumpIfError(hr, error, "pkcsSetExtensionsFromAttributeBlob"); } else if (0 == strcmp(pAttrib->pszObjId, szOID_ENROLLMENT_NAME_VALUE_PAIR)) { // Can't apply name value pair attributes to a renewal or CMC if (PSA_DISALLOW_NAMEVALUEPAIRS & dwDisallowFlags) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "name/value pairs disallowed"); } for (i = 0; i < pAttrib->cValue; i++) { CRYPT_ENROLLMENT_NAME_VALUE_PAIR *pInfo = NULL; DWORD cbInfo = 0; CRYPT_ATTR_BLOB const *pvalue = &pAttrib->rgValue[i]; WCHAR const *pwszPropName; BOOL fConcatenateRDNs; DWORD dwIndex; DWORD cchMax; DWORD dwTable; if (!myDecodeNameValuePair( X509_ASN_ENCODING, pvalue->pbData, pvalue->cbData, CERTLIB_USE_LOCALALLOC, &pInfo, &cbInfo)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "myDecodeNameValuePair"); // if the attribute name & value are both non-empty ... } BOOL fEnrollOnBehalfOf = FALSE; BOOL fSubjectModifiedT = FALSE; hr = pkcsSetAttributeProperty( prow, pInfo->pwszName, pInfo->pwszValue, PROPTABLE_REQUEST, afSubjectTable, &fSubjectModified, NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL); if (fSubjectModifiedT) { fSubjectModified = TRUE; } if (fEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = TRUE; } if (NULL != pInfo) { LocalFree(pInfo); } _JumpIfError(hr, error, "pkcsSetAttributeProperty"); } } else { DBGPRINT(( DBG_SS_CERTSRVI, "Skipping authenticated attribute %hs\n", pAttrib->pszObjId)); } } if (fSubjectModified) { hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED); _JumpIfError(hr, error, "PKCSSetRequestFlags"); } hr = S_OK; error: if (NULL != pBlob) { LocalFree(pBlob); } return(hr); } HRESULT pkcsVerifyCertContext( OPTIONAL IN FILETIME const *pft, IN BOOL fTimeOnly, IN CERT_CONTEXT const *pcc) { HRESULT hr; FILETIME ft; if (NULL == pft) { GetSystemTimeAsFileTime(&ft); pft = &ft; } if (0 > CompareFileTime(pft, &pcc->pCertInfo->NotBefore)) { hr = CERT_E_EXPIRED; _JumpError(hr, error, "cert not yet valid"); } if (0 < CompareFileTime(pft, &pcc->pCertInfo->NotAfter)) { hr = CERT_E_EXPIRED; _JumpError(hr, error, "cert is expired"); } if (!fTimeOnly) { hr = myVerifyCertContext( pcc, // pCert (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags)? CA_VERIFY_FLAGS_IGNORE_OFFLINE : 0, // dwFlags 0, // cUsageOids NULL, // apszUsageOids HCCE_LOCAL_MACHINE, // hChainEngine NULL, // hAdditionalStore NULL); // ppwszMissingIssuer _JumpIfError(hr, error, "myVerifyCertContext"); } hr = S_OK; error: return(hr); } HRESULT pkcsSetValidityPeriod( IN ICertDBRow *prow, IN LONG lValidityPeriodCount, IN enum ENUM_PERIOD enumValidityPeriod) { HRESULT hr; FILETIME ftNotBefore; FILETIME ftNotAfter; LONGLONG delta; CACTX *pCAContext = g_pCAContextCurrent; GetSystemTimeAsFileTime(&ftNotBefore); hr = pkcsVerifyCertContext(&ftNotBefore, TRUE, pCAContext->pccCA); _JumpIfErrorStr(hr, error, "pkcsVerifyCertContext", L"CA cert expired"); ftNotAfter = ftNotBefore; // Set the start date to the current time minus clock skew. But ensure the // new cert's start date is not before the CA certificate's start date. delta = g_dwClockSkewMinutes * CVT_MINUTES; myAddToFileTime(&ftNotBefore, -delta * CVT_BASE); if (0 > CompareFileTime( &ftNotBefore, &pCAContext->pccCA->pCertInfo->NotBefore)) { ftNotBefore = pCAContext->pccCA->pCertInfo->NotBefore; } hr = prow->SetProperty( g_wszPropCertificateNotBeforeDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(ftNotBefore), (BYTE *) &ftNotBefore); _JumpIfError(hr, error, "pkcsSetValidityPeriod:SetProperty"); // Set the end date to the start date plus the registry-configured // validity period. Then clamp the new cert's end date to the CA // certificate's end date. myMakeExprDateTime(&ftNotAfter, lValidityPeriodCount, enumValidityPeriod); if (0 < CompareFileTime( &ftNotAfter, &pCAContext->pccCA->pCertInfo->NotAfter)) { ftNotAfter = pCAContext->pccCA->pCertInfo->NotAfter; } hr = prow->SetProperty( g_wszPropCertificateNotAfterDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(ftNotAfter), (BYTE *) &ftNotAfter); _JumpIfError(hr, error, "pkcsSetValidityPeriod:SetProperty"); error: return(hr); } HRESULT pkcsSetServerExtension( IN ICertDBRow *prow, IN WCHAR const *pwszObjId, IN DWORD cbExt, IN BYTE const *pbExt) { HRESULT hr; BYTE *pbOld = NULL; DWORD cbOld; DWORD ExtFlags; ExtFlags = 0; if (NULL == pbExt) { CSASSERT(0 == cbExt); hr = PropGetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, pwszObjId, &ExtFlags, &cbOld, &pbOld); if (S_OK != hr) { ExtFlags = 0; } if (CERTSRV_E_PROPERTY_EMPTY != hr) { ExtFlags |= EXTENSION_DISABLE_FLAG; } } if (NULL != pbExt || (EXTENSION_DISABLE_FLAG & ExtFlags)) { ExtFlags &= ~EXTENSION_ORIGIN_MASK; ExtFlags |= EXTENSION_ORIGIN_SERVER; hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, pwszObjId, ExtFlags, cbExt, pbExt); _JumpIfError(hr, error, "PropSetExtension"); } hr = S_OK; error: if (NULL != pbOld) { LocalFree(pbOld); } return(hr); } HRESULT PKCSSetServerProperties( IN ICertDBRow *prow, IN LONG lValidityPeriodCount, IN enum ENUM_PERIOD enumValidityPeriod) { HRESULT hr; CACTX *pCAContext = g_pCAContextCurrent; hr = pkcsSetServerExtension( prow, TEXT(szOID_AUTHORITY_KEY_IDENTIFIER2), pCAContext->KeyAuthority2Cert.cbData, pCAContext->KeyAuthority2Cert.pbData); _JumpIfError(hr, error, "pkcsSetServerExtension"); hr = pkcsSetServerExtension( prow, TEXT(szOID_CRL_DIST_POINTS), pCAContext->CDPCert.cbData, pCAContext->CDPCert.pbData); _JumpIfError(hr, error, "pkcsSetServerExtension"); hr = pkcsSetServerExtension( prow, TEXT(szOID_AUTHORITY_INFO_ACCESS), pCAContext->AIACert.cbData, pCAContext->AIACert.pbData); _JumpIfError(hr, error, "pkcsSetServerExtension"); hr = pkcsSetValidityPeriod(prow, lValidityPeriodCount, enumValidityPeriod); _JumpIfError(hr, error, "pkcsSetValidityPeriod"); error: return(hr); } HRESULT pkcsSetPublicKeyProperties( IN ICertDBRow *prow, IN CERT_PUBLIC_KEY_INFO const *pSubjectPublicKeyInfo) { HRESULT hr; WCHAR *pwszExtensionName; WCHAR *pwszObjId = NULL; CACTX *pCAContext = g_pCAContextCurrent; CERT_EXTENSION ext; DWORD ExtFlags; DWORD dwCaller; DWORD cbitKey; ext.Value.pbData = NULL; // Public Key size must be a multiple of 8 bits. if (0 != pSubjectPublicKeyInfo->PublicKey.cUnusedBits) { hr = NTE_BAD_KEY; _JumpError(hr, error, "PublicKey.cUnusedBits"); } hr = prow->SetProperty( g_wszPropCertificateRawPublicKey, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, pSubjectPublicKeyInfo->PublicKey.cbData, pSubjectPublicKeyInfo->PublicKey.pbData); _JumpIfError(hr, error, "SetProperty"); cbitKey = CertGetPublicKeyLength( X509_ASN_ENCODING, const_cast(pSubjectPublicKeyInfo)); hr = prow->SetProperty( g_wszPropCertificatePublicKeyLength, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(cbitKey), (BYTE const *) &cbitKey); _JumpIfError(hr, error, "SetProperty(KeyLength)"); if (!ConvertSzToWsz( &pwszObjId, pSubjectPublicKeyInfo->Algorithm.pszObjId, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertSzToWsz(AlgObjId)"); } hr = prow->SetProperty( g_wszPropCertificatePublicKeyAlgorithm, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE const *) pwszObjId); _JumpIfError(hr, error, "SetProperty"); if (NULL != pSubjectPublicKeyInfo->Algorithm.Parameters.pbData) { hr = prow->SetProperty( g_wszPropCertificateRawPublicKeyAlgorithmParameters, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, pSubjectPublicKeyInfo->Algorithm.Parameters.cbData, pSubjectPublicKeyInfo->Algorithm.Parameters.pbData); _JumpIfError(hr, error, "SetProperty"); } // Subject Key Identifier extension: hr = PropGetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, TEXT(szOID_SUBJECT_KEY_IDENTIFIER), &ExtFlags, &ext.Value.cbData, &ext.Value.pbData); if (CERTSRV_E_PROPERTY_EMPTY != hr) { _JumpIfError(hr, error, "PropGetExtension"); dwCaller = PROPCALLER_REQUEST; ExtFlags &= ~EXTENSION_DISABLE_FLAG; } else { dwCaller = PROPCALLER_SERVER; ExtFlags = EXTENSION_ORIGIN_SERVER; hr = myCreateSubjectKeyIdentifierExtension( pSubjectPublicKeyInfo, &ext.Value.pbData, &ext.Value.cbData); _JumpIfError(hr, error, "myCreateSubjectKeyIdentifierExtension"); } hr = PropSetExtension( prow, PROPTYPE_BINARY | dwCaller, TEXT(szOID_SUBJECT_KEY_IDENTIFIER), ExtFlags, ext.Value.cbData, ext.Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); error: if (NULL != ext.Value.pbData) { LocalFree(ext.Value.pbData); } if (NULL != pwszObjId) { LocalFree(pwszObjId); } return(hr); } HRESULT pkcsParsePKCS10Request( IN DWORD dwFlags, IN ICertDBRow *prow, IN DWORD cbRequest, IN BYTE const *pbRequest, IN CERT_CONTEXT const *pSigningAuthority, OUT BOOL *pfRenewal, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; DWORD cbCertInfo; CERT_REQUEST_INFO *pRequestInfo = NULL; CRYPT_ATTRIBUTE const *pAttrib; CRYPT_ATTRIBUTE const *pAttribEnd; CRYPT_ATTR_BLOB *pAttrBlob; CERT_CONTEXT const *pOldCert = NULL; DWORD dwRequestFlags = 0; BOOL fRenewal = FALSE; BOOL fSubjectNameSet; CSASSERT(CR_IN_PKCS10 == (CR_IN_FORMATMASK & dwFlags)); CSASSERT( CR_IN_PKCS10 == (CR_IN_FORMATMASK & pResult->dwFlagsTop) || CR_IN_PKCS7 == (CR_IN_FORMATMASK & pResult->dwFlagsTop) || CR_IN_CMC == (CR_IN_FORMATMASK & pResult->dwFlagsTop)); if (!myDecodeObject( X509_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED, pbRequest, cbRequest, CERTLIB_USE_LOCALALLOC, (VOID **) &pRequestInfo, &cbCertInfo)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } // verify with the public key passed in the PKCS10 if (!CryptVerifyCertificateSignature( NULL, X509_ASN_ENCODING, const_cast(pbRequest), cbRequest, &pRequestInfo->SubjectPublicKeyInfo)) { hr = myHLastError(); _PrintError3( hr, "CryptVerifyCertificateSignature", E_INVALIDARG, CRYPT_E_ASN1_BADTAG); if (CR_IN_CMC == (CR_IN_FORMATMASK & pResult->dwFlagsTop)) { if (E_INVALIDARG == hr) // NULL signature? { CRYPT_DATA_BLOB Blob; Blob.cbData = cbRequest; Blob.pbData = const_cast(pbRequest); if (!CryptVerifyCertificateSignatureEx( NULL, // hCryptProv X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &Blob, CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL, NULL, // pvIssuer 0, // dwFlags NULL)) // pvReserved { HRESULT hr2 = myHLastError(); _PrintError(hr2, "CryptVerifyCertificateSignatureEx"); } else { hr = S_OK; } } else if ((CRLF_ACCEPT_OLDRFC_CMC & g_dwCRLFlags) && CRYPT_E_ASN1_BADTAG == hr) // No signature? { hr = S_OK; } } if (E_INVALIDARG == hr || CRYPT_E_ASN1_BADTAG == hr) { hr = NTE_BAD_SIGNATURE; } _JumpIfError(hr, error, "CryptVerifyCertificateSignature"); } // handle renewal certificate extensions BEFORE processing the rest of // the request attributes (which may also contain extensions) pAttribEnd = &pRequestInfo->rgAttribute[pRequestInfo->cAttribute]; for (pAttrib = pRequestInfo->rgAttribute; pAttrib < pAttribEnd; pAttrib++) { if (0 == strcmp(pAttrib->pszObjId, szOID_RENEWAL_CERTIFICATE)) { hr = CERTSRV_E_BAD_RENEWAL_CERT_ATTRIBUTE; if (fRenewal) { _JumpError(hr, error, "Multiple renewal certs!"); } if (CR_IN_PKCS7 != (CR_IN_FORMATMASK & pResult->dwFlagsTop) && CR_IN_CMC != (CR_IN_FORMATMASK & pResult->dwFlagsTop)) { _JumpError(hr, error, "renewal cert must be in PKCS7 or CMC"); } if (1 != pAttrib->cValue) { _JumpError(hr, error, "Attribute Value count != 1"); } pAttrBlob = pAttrib->rgValue; pOldCert = CertCreateCertificateContext( X509_ASN_ENCODING, pAttrBlob->pbData, pAttrBlob->cbData); if (NULL == pOldCert) { _JumpError(hr, error, "CertCreateCertificateContext"); } // The old raw certificate, and the signer of the PKCS7 must match! if (NULL == pSigningAuthority || !CertCompareCertificate( pSigningAuthority->dwCertEncodingType, pSigningAuthority->pCertInfo, pOldCert->pCertInfo)) { _JumpError(hr, error, "CertCompareCertificate"); } // This is a renewal, mark it as such. hr = prow->SetProperty( g_wszPropRequestRawOldCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, pAttrBlob->cbData, pAttrBlob->pbData); _JumpIfError(hr, error, "SetProperty(old cert)"); hr = pkcsSetExtensions( prow, EXTENSION_ORIGIN_RENEWALCERT | EXTENSION_DISABLE_FLAG, pOldCert->pCertInfo->rgExtension, pOldCert->pCertInfo->cExtension); _JumpIfError(hr, error, "pkcsSetExtensions(old cert)"); fRenewal = TRUE; } } // handle certificate extensions/known atributes hr = pkcsSetAttributes( prow, EXTENSION_ORIGIN_REQUEST, PSA_DISALLOW_ARCHIVEDKEY, pRequestInfo->rgAttribute, pRequestInfo->cAttribute, 0, NULL, NULL, pResult); _JumpIfError(hr, error, "pkcsSetAttributes(PKCS10)"); hr = pkcsSetRequestNameInfo( prow, &pRequestInfo->Subject, NULL, // pwszCNSuffix &dwRequestFlags, &fSubjectNameSet); _JumpIfError(hr, error, "pkcsSetRequestNameInfo"); if (fSubjectNameSet) { dwRequestFlags |= CR_FLG_SUBJECTUNMODIFIED; } if (fRenewal) { if (!fSubjectNameSet) { CSASSERT(NULL != pOldCert); CSASSERT(NULL != pOldCert->pCertInfo); hr = pkcsSetRequestNameInfo( prow, &pOldCert->pCertInfo->Subject, NULL, // pwszCNSuffix &dwRequestFlags, &fSubjectNameSet); _JumpIfError(hr, error, "pkcsSetRequestNameInfo"); } dwRequestFlags |= CR_FLG_RENEWAL; } hr = prow->SetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwRequestFlags), (BYTE const *) &dwRequestFlags); _JumpIfError(hr, error, "SetProperty(RequestFlags)"); hr = pkcsSetPublicKeyProperties(prow, &pRequestInfo->SubjectPublicKeyInfo); _JumpIfError(hr, error, "pkcsSetPublicKeyProperties"); hr = PKCSSetServerProperties( prow, g_lValidityPeriodCount, g_enumValidityPeriod); _JumpIfError(hr, error, "PKCSSetServerProperties"); if (NULL != pfRenewal) { *pfRenewal = fRenewal; } error: if (NULL != pOldCert) { CertFreeCertificateContext(pOldCert); } if (NULL != pRequestInfo) { LocalFree(pRequestInfo); } return(hr); } HRESULT PKCSVerifyChallengeString( IN ICertDBRow *prow) { HRESULT hr; DWORD cb; WCHAR wszPassed[MAX_PATH]; WCHAR wszExpected[MAX_PATH]; cb = sizeof(wszExpected); hr = prow->GetProperty( g_wszPropExpectedChallenge, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, &cb, (BYTE *) wszExpected); if (S_OK != hr || L'\0' == wszExpected[0]) { hr = S_OK; // no challenge expected goto error; } cb = sizeof(wszPassed); hr = prow->GetProperty( g_wszPropChallenge, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, &cb, (BYTE *) wszPassed); if (S_OK != hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD); _JumpError(hr, error, "Missing Challenge String"); } if (0 != wcscmp(wszExpected, wszPassed)) { CONSOLEPRINT2(( DBG_SS_CERTSRV, "Challenge: passed(%ws) expected(%ws)\n", wszPassed, wszExpected)); hr = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD); _JumpError(hr, error, "Invalid Challenge String"); } hr = prow->SetProperty( g_wszPropExpectedChallenge, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, 0, NULL); _PrintIfError(hr, "SetProperty"); hr = S_OK; error: return(hr); } HRESULT pkcsParseKeyGenRequest( IN DWORD dwFlags, IN ICertDBRow *prow, IN DWORD cbRequest, IN BYTE const *pbRequest, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; DWORD cbKeyGenRequest; CERT_KEYGEN_REQUEST_INFO *pKeyGenRequest = NULL; DWORD dwRequestFlags; CSASSERT(CR_IN_KEYGEN == (CR_IN_FORMATMASK & dwFlags)); // Decode KeyGenRequest structure if (!myDecodeKeyGenRequest( pbRequest, cbRequest, CERTLIB_USE_LOCALALLOC, &pKeyGenRequest, &cbKeyGenRequest)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeKeyGen"); } // verify with the public key passed in the PKCS10 if (!CryptVerifyCertificateSignature( NULL, X509_ASN_ENCODING, (BYTE *) pbRequest, cbRequest, &pKeyGenRequest->SubjectPublicKeyInfo)) { hr = myHLastError(); _JumpError(hr, error, "CryptVerifyCertificateSignature"); } hr = pkcsSetPublicKeyProperties( prow, &pKeyGenRequest->SubjectPublicKeyInfo); _JumpIfError(hr, error, "pkcsSetPublicKeyProperties"); hr = PKCSSetServerProperties( prow, g_lValidityPeriodCount, g_enumValidityPeriod); _JumpIfError(hr, error, "PKCSSetServerProperties"); hr = prow->SetProperty( g_wszPropExpectedChallenge, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_ATTRIBUTE, MAXDWORD, (BYTE *) pKeyGenRequest->pwszChallengeString); _JumpIfError(hr, error, "SetProperty"); dwRequestFlags = 0; switch (ENUM_TELETEX_MASK & g_fForceTeletex) { case ENUM_TELETEX_ON: case ENUM_TELETEX_AUTO: dwRequestFlags |= CR_FLG_FORCETELETEX; break; } if (ENUM_TELETEX_UTF8 & g_fForceTeletex) { dwRequestFlags |= CR_FLG_FORCEUTF8; } hr = prow->SetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwRequestFlags), (BYTE const *) &dwRequestFlags); _JumpIfError(hr, error, "SetProperty(RequestFlags)"); error: if (NULL != pKeyGenRequest) { LocalFree(pKeyGenRequest); } return(hr); } // Validate the certificate: // Signed by CA Certificate // issuer name == CA Certificate subject // NotBefore >= CA Certificate NotBefore // NotAfter <= CA Certificate NotAfter // if KEYID2 issuer KeyId set: == CA Certificate KeyId // if KEYID2 issuer Name set: == CA Certificate Issuer // if KEYID2 issuer Serial Number set: == CA Certificate serial number HRESULT pkcsVerifyCertIssuer( IN CERT_CONTEXT const *pCert, IN CACTX const *pCAContext) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); CERT_INFO const *pCertInfo = pCert->pCertInfo; CERT_INFO const *pCACertInfo = pCAContext->pccCA->pCertInfo; CERT_EXTENSION const *pExt; CERT_EXTENSION const *pExtEnd; CERT_AUTHORITY_KEY_ID2_INFO *pkeyAuth = NULL; DWORD cbkeyAuth; CERT_NAME_BLOB const *pName; // verify with the CA cert's public key if (!CryptVerifyCertificateSignature( NULL, X509_ASN_ENCODING, pCert->pbCertEncoded, pCert->cbCertEncoded, const_cast( &pCACertInfo->SubjectPublicKeyInfo))) { hr = myHLastError(); _JumpError2( hr, error, "CryptVerifyCertificateSignature", NTE_BAD_SIGNATURE); } // Check Issuer name: if (!myAreBlobsSame( pCACertInfo->Subject.pbData, pCACertInfo->Subject.cbData, pCertInfo->Issuer.pbData, pCertInfo->Issuer.cbData)) { _JumpError(hr, error, "Bad Issuer Name"); } // Check that NotBefore >= CA Certificate NotBefore if (0 > CompareFileTime(&pCertInfo->NotBefore, &pCACertInfo->NotBefore)) { _JumpError(hr, error, "NotBefore too early"); } // Check that NotAfter <= CA Certificate NotAfter if (0 < CompareFileTime(&pCertInfo->NotAfter, &pCACertInfo->NotAfter)) { _JumpError(hr, error, "NotAfter too late"); } pExtEnd = &pCert->pCertInfo->rgExtension[pCert->pCertInfo->cExtension]; for (pExt = pCert->pCertInfo->rgExtension; pExt < pExtEnd; pExt++) { if (0 == strcmp(pExt->pszObjId, szOID_AUTHORITY_KEY_IDENTIFIER2)) { if (!myDecodeObject( X509_ASN_ENCODING, X509_AUTHORITY_KEY_ID2, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pkeyAuth, &cbkeyAuth)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } // Check Issuer KeyId: if (NULL != pCAContext->IssuerKeyId.pbData && NULL != pkeyAuth->KeyId.pbData && !myAreBlobsSame( pCAContext->IssuerKeyId.pbData, pCAContext->IssuerKeyId.cbData, pkeyAuth->KeyId.pbData, pkeyAuth->KeyId.cbData)) { _JumpError(hr, error, "Bad AuthorityKeyId KeyId"); } // Check Issuer name: if (1 == pkeyAuth->AuthorityCertIssuer.cAltEntry && CERT_ALT_NAME_DIRECTORY_NAME == pkeyAuth->AuthorityCertIssuer.rgAltEntry[0].dwAltNameChoice) { pName = &pkeyAuth->AuthorityCertIssuer.rgAltEntry[0].DirectoryName; if (NULL != pName->pbData && !myAreBlobsSame( pCACertInfo->Issuer.pbData, pCACertInfo->Issuer.cbData, pName->pbData, pName->cbData)) { _JumpError(hr, error, "Bad AuthorityKeyId Issuer Name"); } } // Check Issuer SerialNumber: if (NULL != pkeyAuth->AuthorityCertSerialNumber.pbData && !myAreSerialNumberBlobsSame( &pCACertInfo->SerialNumber, &pkeyAuth->AuthorityCertSerialNumber)) { _JumpError(hr, error, "Bad AuthorityKeyId Issuer Serial Number"); } break; } } hr = S_OK; error: if (NULL != pkeyAuth) { LocalFree(pkeyAuth); } return(hr); } HRESULT PKCSVerifyIssuedCertificate( IN CERT_CONTEXT const *pCert, OUT CACTX **ppCAContext) { HRESULT hr; DWORD i; CACTX *pCAContext; *ppCAContext = NULL; CSASSERT(0 != g_cCACerts); for (i = g_cCACerts; i > 0; i--) { pCAContext = &g_aCAContext[i - 1]; hr = pkcsVerifyCertIssuer(pCert, pCAContext); if (S_OK == hr) { *ppCAContext = pCAContext; break; } _PrintError2(hr, "pkcsVerifyCertIssuer", NTE_BAD_SIGNATURE); } //error: return(hr); } HRESULT pkcsSetCertHash( IN ICertDBRow *prow, IN CERT_CONTEXT const *pcc) { HRESULT hr; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; DWORD cbHash; BSTR strHash = NULL; CACTX *pCAContext = g_pCAContextCurrent; cbHash = sizeof(abHash); if (!CertGetCertificateContextProperty( pcc, CERT_SHA1_HASH_PROP_ID, abHash, &cbHash)) { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateContextProperty"); } hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); hr = prow->SetProperty( g_wszPropCertificateHash, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE const *) strHash); _JumpIfError(hr, error, "SetProperty"); error: if (NULL != strHash) { SysFreeString(strHash); } return(hr); } HRESULT pkcsSetCertAndKeyHashes( IN ICertDBRow *prow, IN CERT_CONTEXT const *pcc) { HRESULT hr; BYTE *pbHash = NULL; DWORD cbHash; BSTR strHash = NULL; hr = pkcsSetCertHash(prow, pcc); _JumpIfError(hr, error, "pkcsSetCertHash"); hr = myGetPublicKeyHash( pcc->pCertInfo, &pcc->pCertInfo->SubjectPublicKeyInfo, &pbHash, &cbHash); _JumpIfError(hr, error, "myGetPublicKeyHash"); hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); hr = prow->SetProperty( g_wszPropCertificateSubjectKeyIdentifier, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE const *) strHash); _JumpIfError(hr, error, "SetProperty"); error: if (NULL != strHash) { SysFreeString(strHash); } if (NULL != pbHash) { LocalFree(pbHash); } return(hr); } HRESULT PKCSParseImportedCertificate( IN DWORD Disposition, IN ICertDBRow *prow, OPTIONAL IN CACTX const *pCAContext, IN CERT_CONTEXT const *pCert) { HRESULT hr; CERT_INFO const *pCertInfo = pCert->pCertInfo; DWORD dwRequestFlags = 0; BOOL fSubjectNameSet; HRESULT ErrCode = S_OK; BSTR strSerialNumber = NULL; // set raw cert property in the db hr = prow->SetProperty( g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, pCert->cbCertEncoded, pCert->pbCertEncoded); _JumpIfError(hr, error, "SetProperty"); // set extensions hr = pkcsSetExtensions( prow, EXTENSION_ORIGIN_IMPORTEDCERT, pCertInfo->rgExtension, pCertInfo->cExtension); _JumpIfError(hr, error, "pkcsSetExtensions"); // set request name info hr = pkcsSetRequestNameInfo( prow, &pCertInfo->Subject, NULL, // pwszCNSuffix &dwRequestFlags, &fSubjectNameSet); _JumpIfError(hr, error, "pkcsSetRequestNameInfo"); hr = pkcsSetPublicKeyProperties(prow, &pCertInfo->SubjectPublicKeyInfo); _JumpIfError(hr, error, "pkcsSetPublicKeyProperties"); hr = prow->CopyRequestNames(); _JumpIfError(hr, error, "CopyRequestNames"); hr = pkcsSetCertAndKeyHashes(prow, pCert); _JumpIfError(hr, error, "pkcsSetCertAndKeyHashes"); hr = prow->SetProperty( g_wszPropCertificateNotBeforeDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(pCertInfo->NotBefore), (BYTE *) &pCertInfo->NotBefore); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( g_wszPropCertificateNotAfterDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(pCertInfo->NotAfter), (BYTE *) &pCertInfo->NotAfter); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( g_wszPropSubjectRawName, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, pCertInfo->Subject.cbData, pCertInfo->Subject.pbData); _JumpIfError(hr, error, "SetProperty"); // set distinguished name pkcsSetDistinguishedName( prow, PROPTABLE_CERTIFICATE, &pCertInfo->Subject); // set disposition issued hr = prow->SetProperty( g_wszPropRequestDisposition, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(Disposition), (BYTE const *) &Disposition); _JumpIfError(hr, error, "SetProperty(disposition)"); // set disposition status code hr = prow->SetProperty( g_wszPropRequestStatusCode, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(ErrCode), (BYTE const *) &ErrCode); _JumpIfError(hr, error, "SetProperty(status code)"); if (NULL != pCAContext) { hr = prow->SetProperty( g_wszPropCertificateIssuerNameID, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(pCAContext->NameId), (BYTE *) &pCAContext->NameId); _JumpIfError(hr, error, "SetProperty"); } hr = PropSetRequestTimeProperty(prow, g_wszPropRequestResolvedWhen); _JumpIfError(hr, error, "PropSetRequestTimeProperty"); // Convert serial number to string and set in DB hr = MultiByteIntegerToBstr( FALSE, pCertInfo->SerialNumber.cbData, pCertInfo->SerialNumber.pbData, &strSerialNumber); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); hr = prow->SetProperty( g_wszPropCertificateSerialNumber, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE *) strSerialNumber); _JumpIfError(hr, error, "SetProperty"); error: if (NULL != strSerialNumber) { SysFreeString(strSerialNumber); } return(hr); } // Return TRUE if Data and Cert references apply to the specified CMC message BOOL pkcsCMCReferenceMatch( IN DWORD DataReference, // nested CMC message Body Part Id IN DWORD CertReference, // PKCS10 Cert Request Body Part Id IN DWORD dwCmcDataReference, IN DWORD cCertReference, IN DWORD const *rgdwCertReference) { BOOL fMatch = FALSE; DWORD i; if (MAXDWORD != DataReference && dwCmcDataReference == DataReference) { fMatch = TRUE; } else if (MAXDWORD != CertReference && 0 == dwCmcDataReference) { for (i = 0; i < cCertReference; i++) { if (rgdwCertReference[i] == CertReference) { fMatch = TRUE; break; } } } return(fMatch); } HRESULT pkcsSetCMCExtensions( IN ICertDBRow *prow, IN DWORD DataReference, // nested CMC message Body Part Id IN DWORD CertReference, // PKCS10 Cert Request Body Part Id IN BYTE const *pbData, IN DWORD cbData) { HRESULT hr; CMC_ADD_EXTENSIONS_INFO *pcmcExt = NULL; DWORD cb; // Decode CMC_ADD_EXTENSIONS_INFO from Attribute Blob CSASSERT(NULL == pcmcExt); if (!myDecodeObject( X509_ASN_ENCODING, CMC_ADD_EXTENSIONS, pbData, cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pcmcExt, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } if (pkcsCMCReferenceMatch( DataReference, CertReference, pcmcExt->dwCmcDataReference, pcmcExt->cCertReference, pcmcExt->rgdwCertReference)) { hr = pkcsSetExtensions( prow, EXTENSION_ORIGIN_CMC | EXTENSION_DISABLE_FLAG, pcmcExt->rgExtension, pcmcExt->cExtension); _JumpIfError(hr, error, "pkcsSetExtensions(request)"); } hr = S_OK; error: if (NULL != pcmcExt) { LocalFree(pcmcExt); } return(hr); } HRESULT pkcsSetCMCAttributes( IN ICertDBRow *prow, IN DWORD DataReference, // nested CMC message Body Part Id IN DWORD CertReference, // PKCS10 Cert Request Body Part Id IN BYTE const *pbData, IN DWORD cbData, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; CMC_ADD_ATTRIBUTES_INFO *pcmcAttrib = NULL; DWORD cb; if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } // Decode CMC_ADD_ATTRIBUTES_INFO from Attribute Blob CSASSERT(NULL == pcmcAttrib); if (!myDecodeObject( X509_ASN_ENCODING, CMC_ADD_ATTRIBUTES, pbData, cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pcmcAttrib, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } if (pkcsCMCReferenceMatch( DataReference, CertReference, pcmcAttrib->dwCmcDataReference, pcmcAttrib->cCertReference, pcmcAttrib->rgdwCertReference)) { hr = pkcsSetAttributes( prow, EXTENSION_ORIGIN_CMC, PSA_DISALLOW_EXTENSIONS | PSA_DISALLOW_ARCHIVEDKEY, pcmcAttrib->rgAttribute, pcmcAttrib->cAttribute, 0, NULL, pfEnrollOnBehalfOf, pResult); _JumpIfError(hr, error, "pkcsSetAttributes(CMC)"); } hr = S_OK; error: if (NULL != pcmcAttrib) { LocalFree(pcmcAttrib); } return(hr); } // map "email_mail" to "email" // map "email_*" to "*"? // mail_firstName=Terry&mail_lastName=Cheung+CMC+Zero+2&mail_email= // tcheung%40verisign%2Ecom&challenge=test& HRESULT pkcsSetCMCRegInfo( IN ICertDBRow *prow, IN BYTE const *pbOctet, IN DWORD cbOctet, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf) { HRESULT hr; WCHAR *pwszRA = NULL; if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } hr = myDecodeCMCRegInfo(pbOctet, cbOctet, &pwszRA); _JumpIfError(hr, error, "myDecodeCMCRegInfo"); hr = PKCSParseAttributes( prow, pwszRA, TRUE, PROPTABLE_REQUEST, pfEnrollOnBehalfOf); _JumpIfError(hr, error, "PKCSParseAttributes"); error: if (NULL != pwszRA) { LocalFree(pwszRA); } return(hr); } HRESULT pkcsSetTaggedAttributes( IN ICertDBRow *prow, IN DWORD DataReference, // nested CMC message Body Part Id IN DWORD CertReference, // PKCS10 Cert Request Body Part Id IN CMC_TAGGED_ATTRIBUTE const *pTaggedAttribute, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; DWORD i; CRYPT_ATTRIBUTE const *pAttribute = &pTaggedAttribute->Attribute; DWORD cb; BOOL fEnrollOnBehalfOf; if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } for (i = 0; i < pAttribute->cValue; i++) { if (0 == strcmp(szOID_CMC_ADD_EXTENSIONS, pAttribute->pszObjId)) { hr = pkcsSetCMCExtensions( prow, DataReference, CertReference, pAttribute->rgValue[i].pbData, pAttribute->rgValue[i].cbData); _JumpIfError(hr, error, "pkcsSetCMCExtensions"); } else if (0 == strcmp(szOID_CMC_ADD_ATTRIBUTES, pAttribute->pszObjId)) { fEnrollOnBehalfOf = FALSE; hr = pkcsSetCMCAttributes( prow, DataReference, CertReference, pAttribute->rgValue[i].pbData, pAttribute->rgValue[i].cbData, NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL, pResult); _JumpIfError(hr, error, "pkcsSetCMCAttributes"); if (fEnrollOnBehalfOf) { CSASSERT(NULL != pfEnrollOnBehalfOf); *pfEnrollOnBehalfOf = TRUE; } } else if (0 == strcmp(szOID_CMC_REG_INFO, pAttribute->pszObjId)) { fEnrollOnBehalfOf = FALSE; hr = pkcsSetCMCRegInfo( prow, pAttribute->rgValue[i].pbData, pAttribute->rgValue[i].cbData, NULL != pfEnrollOnBehalfOf? &fEnrollOnBehalfOf : NULL); _JumpIfError(hr, error, "pkcsSetCMCRegInfo"); if (fEnrollOnBehalfOf) { CSASSERT(NULL != pfEnrollOnBehalfOf); *pfEnrollOnBehalfOf = TRUE; } } else if (0 == strcmp(szOID_CMC_TRANSACTION_ID, pAttribute->pszObjId)) { DWORD dwTransactionId; cb = sizeof(dwTransactionId); dwTransactionId = 0; if (!CryptDecodeObject( X509_ASN_ENCODING, X509_INTEGER, pAttribute->rgValue[i].pbData, pAttribute->rgValue[i].cbData, 0, &dwTransactionId, &cb)) { hr = myHLastError(); _JumpError(hr, error, "CryptDecodeObject"); } pResult->fTransactionId = TRUE; pResult->dwTransactionId = dwTransactionId; } else if (0 == strcmp(szOID_CMC_SENDER_NONCE, pAttribute->pszObjId)) { CRYPT_DATA_BLOB *pBlob; BYTE *pb; if (!myDecodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, pAttribute->rgValue[i].pbData, pAttribute->rgValue[i].cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pBlob, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } pb = (BYTE *) LocalAlloc(LMEM_FIXED, pBlob->cbData); if (NULL == pb) { hr = E_OUTOFMEMORY; } else { CopyMemory(pb, pBlob->pbData, pBlob->cbData); if (NULL != pResult->pbSenderNonce) { LocalFree(pResult->pbSenderNonce); } pResult->pbSenderNonce = pb; pResult->cbSenderNonce = pBlob->cbData; hr = S_OK; } LocalFree(pBlob); _JumpIfError(hr, error, "LocalAlloc"); } else if (0 == (CRLF_IGNORE_UNKNOWN_CMC_ATTRIBUTES & g_dwCRLFlags)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "unknown tagged attribute"); } } hr = S_OK; error: return(hr); } //+------------------------------------------------------------------------ // pkcsParseCMCRequest // // Crack a CMC request and dig the goodies out of it. // Crack the contents of the CMC request recursively. //------------------------------------------------------------------------- HRESULT pkcsParseCMCRequest( IN ICertDBRow *prow, IN DWORD cbIn, IN BYTE const *pbIn, OPTIONAL IN CERT_CONTEXT const *pCertSigner, OPTIONAL OUT BOOL *pfRenewal, OPTIONAL OUT BOOL *pfEnrollOnBehalfOf, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; DWORD cb; CMC_DATA_INFO const *pcmcData = NULL; DWORD i; DWORD DataReference = MAXDWORD; // nested CMC message Body Part Id DWORD CertReference = MAXDWORD; // PKCS10 Cert Request Body Part Id if (NULL != pfRenewal) { *pfRenewal = FALSE; } if (NULL != pfEnrollOnBehalfOf) { *pfEnrollOnBehalfOf = FALSE; } if (!myDecodeObject( X509_ASN_ENCODING, CMC_DATA, pbIn, cbIn, CERTLIB_USE_LOCALALLOC, (VOID **) &pcmcData, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } if (0 != pcmcData->cTaggedOtherMsg) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "unknown other message"); } // Process nested CMC messages if (0 != pcmcData->cTaggedContentInfo) { CMC_TAGGED_CONTENT_INFO const *pTaggedContentInfo; // Only handle one CMC message at a time for now. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); if (1 < pcmcData->cTaggedContentInfo) { _JumpError(hr, error, "multiple nested CMC messages"); } // Disallow CMC message recursion below a PKCS10 request. if (0 != pcmcData->cTaggedRequest) { _JumpError(hr, error, "recursion below PKCS10 request"); } // Recurse on the nested CMC message pTaggedContentInfo = &pcmcData->rgTaggedContentInfo[0]; hr = PKCSParseRequest( CR_IN_CMC | (~CR_IN_FORMATMASK & pResult->dwFlagsTop), prow, pTaggedContentInfo->EncodedContentInfo.cbData, pTaggedContentInfo->EncodedContentInfo.pbData, pCertSigner, pfRenewal, pResult); _JumpIfError(hr, error, "PKCSParseRequest"); DataReference = pTaggedContentInfo->dwBodyPartID; } // Process nested PKCS10 requests if (0 != pcmcData->cTaggedRequest) { CMC_TAGGED_REQUEST const *pTaggedRequest; CMC_TAGGED_CERT_REQUEST const *pTaggedCertRequest; // Only handle one request at a time for now. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); if (1 < pcmcData->cTaggedRequest) { _JumpError(hr, error, "multiple PKCS10 requests"); } pTaggedRequest = &pcmcData->rgTaggedRequest[0]; // The request must be a PKCS10 request if (CMC_TAGGED_CERT_REQUEST_CHOICE != pTaggedRequest->dwTaggedRequestChoice) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "recursion below PKCS10 request"); } pTaggedCertRequest = pTaggedRequest->pTaggedCertRequest; hr = PKCSParseRequest( CR_IN_PKCS10 | (~CR_IN_FORMATMASK & pResult->dwFlagsTop), prow, pTaggedCertRequest->SignedCertRequest.cbData, pTaggedCertRequest->SignedCertRequest.pbData, pCertSigner, pfRenewal, pResult); _JumpIfError(hr, error, "PKCSParseRequest"); CertReference = pTaggedCertRequest->dwBodyPartID; } // Process extensions and attributes for (i = 0; i < pcmcData->cTaggedAttribute; i++) { hr = pkcsSetTaggedAttributes( prow, DataReference, CertReference, &pcmcData->rgTaggedAttribute[i], pfEnrollOnBehalfOf, pResult); _JumpIfError(hr, error, "pkcsSetTaggedAttributes"); } hr = S_OK; error: if (NULL != pcmcData) { LocalFree((VOID *) pcmcData); } return(hr); } HRESULT pkcsAppendPolicies( IN ICertDBRow *prow, IN WCHAR const *pwszPropName, OPTIONAL IN WCHAR const *pwszzPolicies) { HRESULT hr; WCHAR *pwszOld = NULL; WCHAR *pwszNew = NULL; WCHAR const *pwszIn; WCHAR *pwsz; DWORD cwc = 0; DWORD cb; hr = PKCSGetProperty( prow, pwszPropName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE **) &pwszOld); _PrintIfError2(hr, "PKCSGetProperty", hr); if (S_OK != hr) { pwszOld = NULL; } if (NULL != pwszOld) { cwc = wcslen(pwszOld) + 1; // allow for \n separator } if (NULL == pwszzPolicies || L'\0' == *pwszzPolicies) { pwszzPolicies = L"-\0"; } for (pwszIn = pwszzPolicies; L'\0' != *pwszIn; pwszIn += wcslen(pwszIn) + 1) ; cwc += SAFE_SUBTRACT_POINTERS(pwszIn, pwszzPolicies); pwszNew = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == pwszNew) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwsz = pwszNew; if (NULL != pwszOld) { wcscpy(pwsz, pwszOld); pwsz += wcslen(pwsz); wcscpy(pwsz, L"\n"); pwsz++; } for (pwszIn = pwszzPolicies; L'\0' != *pwszIn; pwszIn += wcslen(pwszIn) + 1) { if (pwszIn != pwszzPolicies) { wcscpy(pwsz, L","); pwsz++; } wcscpy(pwsz, pwszIn); pwsz += wcslen(pwsz); } CSASSERT(&pwsz[1] == &pwszNew[cwc]); hr = prow->SetProperty( pwszPropName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszNew); _JumpIfError(hr, error, "SetProperty"); error: if (NULL != pwszOld) { LocalFree(pwszOld); } if (NULL != pwszNew) { LocalFree(pwszNew); } return(hr); } //+------------------------------------------------------------------------ // pkcsParsePKCS7Request // // Crack a PKCS7 and dig the goodies out of it. // Verify the signature of the 7 against the cert given in the 7. // Crack the contents of the 7 recursively. //------------------------------------------------------------------------- HRESULT pkcsParsePKCS7Request( IN BOOL fTopLevel, IN DWORD dwFlags, IN ICertDBRow *prow, IN DWORD cbIn, IN BYTE const *pbIn, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; BYTE *pbContents = NULL; DWORD cbContents; CERT_CONTEXT const *pCertSigner = NULL; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; char *pszInnerContentObjId = NULL; DWORD i; BOOL fCMC; BOOL fRenewal = FALSE; DWORD cbAttrib; char *apszEnrollOids[] = {szOID_ENROLLMENT_AGENT}; DWORD dwVerifyContextFlags; DWORD dwMsgType; DWORD cSigner; DWORD cFirstSigner; BOOL fFirstSigner; DWORD cRecipient; DWORD cb; CMSG_CMS_SIGNER_INFO *pcsi = NULL; DWORD dwDisallowFlags; DWORD iElement; WCHAR *pwszzIssuancePolicies = NULL; WCHAR *pwszzApplicationPolicies = NULL; CERT_REQUEST_INFO *pRequest = NULL; BOOL fEnrollOnBehalfOf; CSASSERT( CR_IN_PKCS7 == (CR_IN_FORMATMASK & dwFlags) || CR_IN_CMC == (CR_IN_FORMATMASK & dwFlags)); // Crack the 7 and verify the signature. hr = myDecodePKCS7( pbIn, cbIn, &pbContents, &cbContents, &dwMsgType, &pszInnerContentObjId, &cSigner, &cRecipient, &hStore, &hMsg); _JumpIfError(hr, error, "myDecodePKCS7"); if (CMSG_SIGNED != dwMsgType || 0 == cSigner) { hr = CRYPT_E_NO_SIGNER; _JumpIfError(hr, error, "myDecodePKCS7(no signing cert)"); } #define szOID_CT_PKI_DATA_OLDRFC "1.3.6.1.5.5.7.5.2" // BUGBUG: temporary! fCMC = NULL != pszInnerContentObjId && (0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA) || (0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA_OLDRFC) && (CRLF_ACCEPT_OLDRFC_CMC & g_dwCRLFlags))); // Decode the contents. if (fCMC) { if (CR_IN_CMC != (CR_IN_FORMATMASK & dwFlags)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "dwFlags"); } // CMC renewal requests may have one 'first' signer (non-NULL KeyId or // Dummy signer) and one additional Issuer+Serial signer. If the // request has the appropriate signatures, pass the cert to the lowest // level to see if it really is a renewal request. if (1 <= cSigner && 2 >= cSigner) { DWORD iCertSigner = MAXDWORD; cFirstSigner = 0; for (i = 0; i < cSigner; i++) { if (NULL != pcsi) { LocalFree(pcsi); pcsi = NULL; } hr = myCryptMsgGetParam( hMsg, CMSG_CMS_SIGNER_INFO_PARAM, i, CERTLIB_USE_LOCALALLOC, (VOID **) &pcsi, &cb); _JumpIfError(hr, error, "myCryptMsgGetParam"); fFirstSigner = FALSE; if (CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice || (NULL != pcsi->HashEncryptionAlgorithm.pszObjId && 0 == strcmp( szOID_PKIX_NO_SIGNATURE, pcsi->HashEncryptionAlgorithm.pszObjId))) { fFirstSigner = TRUE; } else if ((CRLF_ACCEPT_OLDRFC_CMC & g_dwCRLFlags) && CERT_ID_ISSUER_SERIAL_NUMBER == pcsi->SignerId.dwIdChoice) { hr = myIsFirstSigner( &pcsi->SignerId.IssuerSerialNumber.Issuer, &fFirstSigner); _JumpIfError(hr, error, "myIsFirstSigner"); } if (fFirstSigner) { cFirstSigner++; } else { if (MAXDWORD != iCertSigner) { iCertSigner = MAXDWORD; // must not be a renewal break; } iCertSigner = i; } } if (MAXDWORD != iCertSigner && 1 >= cFirstSigner) { iElement = iCertSigner; if (!CryptMsgGetAndVerifySigner( hMsg, 0, // cSignerStore NULL, // rghSignerStore CMSG_USE_SIGNER_INDEX_FLAG, &pCertSigner, &iElement)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgGetAndVerifySigner"); } } } fEnrollOnBehalfOf = FALSE; hr = pkcsParseCMCRequest( prow, cbContents, pbContents, pCertSigner, &fRenewal, &fEnrollOnBehalfOf, pResult); _JumpIfError(hr, error, "pkcsParseCMCRequest"); if (fEnrollOnBehalfOf) { pResult->fEnrollOnBehalfOf = TRUE; } if (0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA_OLDRFC)) { hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_OLDRFCCMC); _JumpIfError(hr, error, "PKCSSetRequestFlags"); } } else { if (CR_IN_PKCS7 != (CR_IN_FORMATMASK & dwFlags)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "dwFlags"); } // Expect only one signer for PKCS7 renewal requests. Pass the cert // to the lowest level to see if it really is a renewal request. iElement = 0; if (!CryptMsgGetAndVerifySigner( hMsg, 0, // cSignerStore NULL, // rghSignerStore CMSG_USE_SIGNER_INDEX_FLAG, &pCertSigner, &iElement)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgGetAndVerifySigner"); } hr = PKCSParseRequest( CR_IN_FORMATANY | (~CR_IN_FORMATMASK & pResult->dwFlagsTop), prow, cbContents, pbContents, pCertSigner, &fRenewal, pResult); _JumpIfError(hr, error, "PKCSParseRequest"); } // Loop through the signers, verifying signatures and saving attributes. cFirstSigner = 0; for (i = 0; i < cSigner; i++) { BOOL fNTAuth; BOOL fEnrollOnBehalfOf; if (NULL != pcsi) { LocalFree(pcsi); pcsi = NULL; } if (NULL != pwszzIssuancePolicies) { LocalFree(pwszzIssuancePolicies); pwszzIssuancePolicies = NULL; } if (NULL != pwszzApplicationPolicies) { LocalFree(pwszzApplicationPolicies); pwszzApplicationPolicies = NULL; } if (NULL != pCertSigner) { CertFreeCertificateContext(pCertSigner); pCertSigner = NULL; } hr = myCryptMsgGetParam( hMsg, CMSG_CMS_SIGNER_INFO_PARAM, i, CERTLIB_USE_LOCALALLOC, (VOID **) &pcsi, &cb); _JumpIfError(hr, error, "myCryptMsgGetParam"); fFirstSigner = FALSE; if (fCMC && (CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice || (NULL != pcsi->HashEncryptionAlgorithm.pszObjId && 0 == strcmp( szOID_PKIX_NO_SIGNATURE, pcsi->HashEncryptionAlgorithm.pszObjId)))) { CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA cvse; fFirstSigner = TRUE; ZeroMemory(&cvse, sizeof(cvse)); cvse.cbSize = sizeof(cvse); cvse.dwSignerIndex = i; if (CERT_ID_KEY_IDENTIFIER == pcsi->SignerId.dwIdChoice) { if (NULL == pRequest) { hr = myGetInnerPKCS10( hMsg, pszInnerContentObjId, &pRequest); _JumpIfError(hr, error, "myGetInnerPKCS10"); } cvse.dwSignerType = CMSG_VERIFY_SIGNER_PUBKEY; cvse.pvSigner = &pRequest->SubjectPublicKeyInfo; } else { cvse.dwSignerType = CMSG_VERIFY_SIGNER_NULL; } if (!CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE_EX, &cvse)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgControl(VerifySig)"); } } else { if (fCMC && (CRLF_ACCEPT_OLDRFC_CMC & g_dwCRLFlags) && CERT_ID_ISSUER_SERIAL_NUMBER == pcsi->SignerId.dwIdChoice) { hr = myIsFirstSigner( &pcsi->SignerId.IssuerSerialNumber.Issuer, &fFirstSigner); _JumpIfError(hr, error, "myIsFirstSigner"); } iElement = i; if (!CryptMsgGetAndVerifySigner( hMsg, 0, // cSignerStore NULL, // rghSignerStore CMSG_USE_SIGNER_INDEX_FLAG, &pCertSigner, &iElement)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgGetAndVerifySigner"); } } // Only enroll-on-behalf-of requests may contain Name, Value pairs. // Only enroll-on-behalf-of requests and renewal requests may contain // certificate extensions. dwDisallowFlags = PSA_DISALLOW_ARCHIVEDKEY; if (fRenewal) { dwDisallowFlags |= PSA_DISALLOW_NAMEVALUEPAIRS; } if (fCMC) { dwDisallowFlags |= PSA_DISALLOW_EXTENSIONS | PSA_DISALLOW_NAMEVALUEPAIRS; } fEnrollOnBehalfOf = FALSE; hr = pkcsSetAttributes( prow, EXTENSION_ORIGIN_PKCS7, dwDisallowFlags, pcsi->AuthAttrs.rgAttr, pcsi->AuthAttrs.cAttr, 0, NULL, &fEnrollOnBehalfOf, pResult); _JumpIfError(hr, error, "pkcsSetAttributes(Authenticated)"); if (fEnrollOnBehalfOf) { pResult->fEnrollOnBehalfOf = TRUE; } // Pull encrypted private key out of unauthenticated attributes hr = pkcsSetAttributes( prow, EXTENSION_ORIGIN_PKCS7, ((fTopLevel && fFirstSigner)? 0 : PSA_DISALLOW_ARCHIVEDKEY) | PSA_DISALLOW_EXTENSIONS | PSA_DISALLOW_NAMEVALUEPAIRS, pcsi->UnauthAttrs.rgAttr, pcsi->UnauthAttrs.cAttr, cbIn, fTopLevel? pbIn : NULL, NULL, pResult); _JumpIfError(hr, error, "pkcsSetAttributes(UNauthenticated)"); if (fFirstSigner) { cFirstSigner++; } else { // This is a renewal request, an enroll-on-behalf-of request, a CMC // request or just a request inside a PKCS 7 -- verify the cert // chain for all signers. If enroll-on-behalf-of on an Enterprise // CA (if requester name is set in the authenticated attributes), // check the signing cert via NTAuth policy and check for // szOID_ENROLLMENT_AGENT usage. NtAuth verification was added to // control the ability of enroll-on-behalf agents to add usernames // to the PKCS7 wrapper. fNTAuth = pResult->fEnrollOnBehalfOf && IsEnterpriseCA(g_CAType); dwVerifyContextFlags = 0; if (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags) { dwVerifyContextFlags |= CA_VERIFY_FLAGS_IGNORE_OFFLINE; } if (fNTAuth) { dwVerifyContextFlags |= CA_VERIFY_FLAGS_NT_AUTH; } hr = myVerifyCertContextEx( pCertSigner, dwVerifyContextFlags, (DWORD)(fNTAuth? ARRAYSIZE(apszEnrollOids) : 0), fNTAuth? apszEnrollOids : NULL, HCCE_LOCAL_MACHINE, // hChainEngine NULL, // pft hStore, // hAdditionalStore NULL, // ppwszMissingIssuer &pwszzIssuancePolicies, &pwszzApplicationPolicies); _JumpIfError(hr, error, "myVerifyCertContextEx"); if (fTopLevel) { // save Issuance Policies hr = pkcsAppendPolicies( prow, wszPROPSIGNERPOLICIES, pwszzIssuancePolicies); _JumpIfError(hr, error, "pkcsAppendPolicies"); // save Application Policies hr = pkcsAppendPolicies( prow, wszPROPSIGNERAPPLICATIONPOLICIES, pwszzApplicationPolicies); _JumpIfError(hr, error, "pkcsAppendPolicies"); } } } if (pResult->fEnrollOnBehalfOf) { hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_ENROLLONBEHALFOF); _JumpIfError(hr, error, "PKCSSetRequestFlags"); if (fCMC && cSigner == cFirstSigner) { hr = CRYPT_E_NO_TRUSTED_SIGNER; _JumpError(hr, error, "No NTAuth signer"); } } if ((fCMC && 1 < cFirstSigner) || (!fCMC && 0 < cFirstSigner)) { hr = NTE_BAD_SIGNATURE; _JumpError(hr, error, "cFirstSigner"); } error: if (NULL != pRequest) { LocalFree(pRequest); } if (NULL != pcsi) { LocalFree(pcsi); } if (NULL != pwszzIssuancePolicies) { LocalFree(pwszzIssuancePolicies); } if (NULL != pwszzApplicationPolicies) { LocalFree(pwszzApplicationPolicies); } if (NULL != hMsg) { CryptMsgClose(hMsg); } if (NULL != pCertSigner) { CertFreeCertificateContext(pCertSigner); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } if (NULL != pbContents) { LocalFree(pbContents); } if (NULL != pszInnerContentObjId) { LocalFree(pszInnerContentObjId); } return(hr); } typedef struct _REQUESTFORMATS { char const *pszFormat; DWORD dwFlags; } REQUESTFORMATS; REQUESTFORMATS g_arf[] = { { X509_CERT_REQUEST_TO_BE_SIGNED, CR_IN_PKCS10 }, { X509_KEYGEN_REQUEST_TO_BE_SIGNED, CR_IN_KEYGEN }, }; #define CREQUESTFORMATS ARRAYSIZE(g_arf) HRESULT pkcsCrackRequestType( IN DWORD cbRequest, IN BYTE const *pbRequest, OUT DWORD *pdwFlags) { HRESULT hr; DWORD cb; BYTE *pbDecoded = NULL; REQUESTFORMATS const *prf; REQUESTFORMATS const *prfEnd; HCRYPTMSG hMsg = NULL; char *pszInnerContentObjId = NULL; prfEnd = &g_arf[CREQUESTFORMATS]; for (prf = g_arf; prf < prfEnd; prf++) { CSASSERT(NULL == pbDecoded); if (myDecodeObject( X509_ASN_ENCODING, prf->pszFormat, pbRequest, cbRequest, CERTLIB_USE_LOCALALLOC, (VOID **) &pbDecoded, &cb)) { *pdwFlags = prf->dwFlags; break; } hr = myHLastError(); CSASSERT(S_OK != hr); } if (prf >= prfEnd) { CSASSERT(S_OK != hr); hr = myDecodePKCS7( pbRequest, cbRequest, NULL, // ppbContents NULL, // pcbContents NULL, // pdwMsgType &pszInnerContentObjId, NULL, // pcSigner NULL, // pcRecipient NULL, // phStore &hMsg); _JumpIfError(hr, error, "myDecodePKCS7"); *pdwFlags = CR_IN_PKCS7; // default to renewal if (NULL != pszInnerContentObjId && (0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA) || 0 == strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA_OLDRFC))) { *pdwFlags = CR_IN_CMC; } } hr = S_OK; error: if (NULL != hMsg) { CryptMsgClose(hMsg); } if (NULL != pszInnerContentObjId) { LocalFree(pszInnerContentObjId); } if (NULL != pbDecoded) { LocalFree(pbDecoded); } return(hr); } HRESULT PKCSParseRequest( IN DWORD dwFlags, IN ICertDBRow *prow, IN DWORD cbRequest, IN BYTE const *pbRequest, IN CERT_CONTEXT const *pSigningAuthority, OPTIONAL OUT BOOL *pfRenewal, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; if (NULL != pfRenewal) { *pfRenewal = FALSE; } if (CR_IN_FORMATANY == (CR_IN_FORMATMASK & dwFlags)) { hr = pkcsCrackRequestType(cbRequest, pbRequest, &dwFlags); _JumpIfError(hr, error, "pkcsCrackRequestType"); dwFlags |= ~CR_IN_FORMATMASK & pResult->dwFlagsTop; // If this is the top level caller, store a more specific request type: if (NULL == pfRenewal) { pResult->dwFlagsTop = dwFlags; hr = prow->SetProperty( g_wszPropRequestType, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwFlags), (BYTE const *) &dwFlags); _JumpIfError(hr, error, "SetProperty(reqtype)"); } } switch (CR_IN_FORMATMASK & dwFlags) { case CR_IN_PKCS10: hr = pkcsParsePKCS10Request( dwFlags, prow, cbRequest, pbRequest, pSigningAuthority, pfRenewal, pResult); _JumpIfError(hr, error, "pkcsParsePKCS10Request"); break; case CR_IN_KEYGEN: hr = pkcsParseKeyGenRequest( dwFlags, prow, cbRequest, pbRequest, pResult); _JumpIfError(hr, error, "pkcsParseKeyGenRequest"); break; case CR_IN_CMC: case CR_IN_PKCS7: // PKCS7 requests can either be an 'enroll on behalf of', renewal // request or a CMC request. We need to recursively unwrap it to // process it. hr = pkcsParsePKCS7Request( NULL == pfRenewal, // fTopLevel dwFlags, prow, cbRequest, pbRequest, pResult); _JumpIfError(hr, error, "pkcsParsePKCS7Request"); break; default: hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "dwFlags"); } error: if (NULL == pfRenewal) { HRESULT hr2; DWORD cbData; hr2 = prow->GetProperty( g_wszPropRequestRawRequest, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbData, NULL); if (S_OK != hr2) { hr2 = prow->SetProperty( g_wszPropRequestRawRequest, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, cbRequest, pbRequest); _PrintIfError(hr2, "SetProperty(request)"); if (S_OK == hr) { hr = hr2; } } } return(hr); } HRESULT PKCSGetCRLList( IN BOOL fDelta, IN DWORD iCert, OUT WCHAR const * const **ppapwszCRLList) { HRESULT hr = E_INVALIDARG; *ppapwszCRLList = NULL; if (iCert < g_cCACerts) { CACTX *pCAContext = &g_aCAContext[iCert]; if (NULL == pCAContext->pccCA) { hr = S_FALSE; goto error; } *ppapwszCRLList = fDelta? pCAContext->papwszDeltaCRLFiles : pCAContext->papwszCRLFiles; if (NULL != *ppapwszCRLList) { hr = S_OK; } } error: return(hr); } HRESULT pkcsBuildCRLList( IN BOOL fDelta, IN OUT CACTX *pCAContext, OUT WCHAR ***ppapwszOut) { HRESULT hr; DWORD cFiles; CSURLTEMPLATE const *pTemplate; CSURLTEMPLATE const *pTemplateEnd; cFiles = 0; pTemplateEnd = &g_paRevURL[g_caRevURL]; for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++) { if (CSURL_SERVERPUBLISH & pTemplate->Flags) { cFiles++; } } *ppapwszOut = (WCHAR **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, (cFiles + 1) * sizeof((*ppapwszOut)[0])); if (NULL == *ppapwszOut) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } DBGPRINT(( DBG_SS_CERTSRVI, "CRLList alloc[%u] = %x @%x\n", cFiles, *ppapwszOut, ppapwszOut)); CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN); cFiles = 0; for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++) { if (CSURL_SERVERPUBLISH & pTemplate->Flags) { hr = myFormatCertsrvStringArray( FALSE, // fURL g_pwszServerName, // pwszServerName_p1_2 g_wszSanitizedName, // pwszSanitizedName_p3_7 pCAContext->iKey, // iCert_p4 -- use iKey!! g_strDomainDN, // pwszDomainDN_p5 g_strConfigDN, // pwszConfigDN_p6 pCAContext->iKey, // iCRL_p8 fDelta, // fDeltaCRL_p9 FALSE, // fDSAttrib_p10_11 1, // cStrings (LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn &(*ppapwszOut)[cFiles]); // apwszStringsOut _JumpIfError(hr, error, "myFormatCertsrvStringArray"); DBGPRINT(( DBG_SS_CERTSRVI, "CRLList format[%u] = %x @%x (%ws)\n", cFiles, (*ppapwszOut)[cFiles], &(*ppapwszOut)[cFiles], (*ppapwszOut)[cFiles])); cFiles++; } } (*ppapwszOut)[cFiles] = NULL; hr = S_OK; error: // Freeing the CACTX structure during shutdown will free orphaned CRL paths return(hr); } HRESULT pkcsBuildKeyAuthority2( IN DWORD EditFlags, IN CACTX const *pCAContext, OUT CRYPT_OBJID_BLOB *pKeyAuthority2) { HRESULT hr = S_OK; CERT_AUTHORITY_KEY_ID2_INFO keyAuth; CERT_ALT_NAME_ENTRY AltNameEntry; if (0 == ((EDITF_ENABLEAKIKEYID | EDITF_ENABLEAKIISSUERNAME | EDITF_ENABLEAKIISSUERSERIAL) & EditFlags)) { goto error; } ZeroMemory(&keyAuth, sizeof(keyAuth)); // Issuer's KeyId: if ((EDITF_ENABLEAKIKEYID & EditFlags) && NULL != pCAContext->IssuerKeyId.pbData) { keyAuth.KeyId = pCAContext->IssuerKeyId; } // The Issuer's Issuer name and the Issuer's SerialNumber combined // should uniquely identify the Issuer cert. // Issuer's Issuer name: // -------- ------ ---- if (EDITF_ENABLEAKIISSUERNAME & EditFlags) { AltNameEntry.dwAltNameChoice = CERT_ALT_NAME_DIRECTORY_NAME; AltNameEntry.DirectoryName = pCAContext->pccCA->pCertInfo->Issuer; keyAuth.AuthorityCertIssuer.cAltEntry = 1; keyAuth.AuthorityCertIssuer.rgAltEntry = &AltNameEntry; } // Issuer's SerialNumber: if (EDITF_ENABLEAKIISSUERSERIAL & EditFlags) { keyAuth.AuthorityCertSerialNumber = pCAContext->pccCA->pCertInfo->SerialNumber; } // put in Key Authority Info if (!myEncodeKeyAuthority2( X509_ASN_ENCODING, &keyAuth, CERTLIB_USE_LOCALALLOC, &pKeyAuthority2->pbData, &pKeyAuthority2->cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeKeyAuthority2"); } error: return(hr); } HRESULT pkcsBuildCDP( IN DWORD Flags, IN BOOL fDelta, IN CACTX const *pCAContext, OUT CRYPT_OBJID_BLOB *pCDP) { HRESULT hr; DWORD i; CSURLTEMPLATE const *pTemplate; CSURLTEMPLATE const *pTemplateEnd; CRL_DIST_POINTS_INFO CRLDistInfo; CRL_DIST_POINT CRLDistPoint; CERT_ALT_NAME_INFO *pAltInfo; ZeroMemory(&CRLDistPoint, sizeof(CRLDistPoint)); pAltInfo = &CRLDistPoint.DistPointName.FullName; pCDP->pbData = NULL; pCDP->cbData = 0; if (0 != g_caRevURL) { pTemplateEnd = &g_paRevURL[g_caRevURL]; for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++) { if (Flags & pTemplate->Flags) { pAltInfo->cAltEntry++; } } } if (0 == pAltInfo->cAltEntry) { hr = S_FALSE; goto error; } CRLDistInfo.cDistPoint = 1; CRLDistInfo.rgDistPoint = &CRLDistPoint; CRLDistPoint.DistPointName.dwDistPointNameChoice = CRL_DIST_POINT_FULL_NAME; pAltInfo->rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, pAltInfo->cAltEntry * sizeof(pAltInfo->rgAltEntry[0])); if (NULL == pAltInfo->rgAltEntry) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN); i = 0; for (pTemplate = g_paRevURL; pTemplate < pTemplateEnd; pTemplate++) { if (Flags & pTemplate->Flags) { hr = myFormatCertsrvStringArray( TRUE, // fURL g_pwszServerName, // pwszServerName_p1_2 g_wszSanitizedName, // pwszSanitizedName_p3_7 pCAContext->iCert, // iCert_p4 g_strDomainDN, // pwszDomainDN_p5 g_strConfigDN, // pwszConfigDN_p6 pCAContext->iKey, // iCRL_p8 fDelta, // fDeltaCRL_p9 TRUE, // fDSAttrib_p10_11 1, // cStrings (LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn &pAltInfo->rgAltEntry[i].pwszURL); // apwszStringsOut _JumpIfError(hr, error, "myFormatCertsrvStringArray"); pAltInfo->rgAltEntry[i].dwAltNameChoice = CERT_ALT_NAME_URL; i++; } } CSASSERT(pAltInfo->cAltEntry == i); if (!myEncodeObject( X509_ASN_ENCODING, X509_CRL_DIST_POINTS, &CRLDistInfo, 0, CERTLIB_USE_LOCALALLOC, &pCDP->pbData, &pCDP->cbData)) { hr = myHLastError(); _JumpIfError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (NULL != pAltInfo->rgAltEntry) { for (i = 0; i < pAltInfo->cAltEntry; i++) { if (NULL != pAltInfo->rgAltEntry[i].pwszURL) { LocalFree(pAltInfo->rgAltEntry[i].pwszURL); } } LocalFree(pAltInfo->rgAltEntry); } return(hr); } HRESULT pkcsBuildAIA( IN DWORD Flags, IN CACTX const *pCAContext, OUT CRYPT_OBJID_BLOB *pAIA) { HRESULT hr; DWORD cAIA; DWORD i; CSURLTEMPLATE const *pTemplate; CSURLTEMPLATE const *pTemplateEnd; CERT_AUTHORITY_INFO_ACCESS caio; CERT_ACCESS_DESCRIPTION *pcad; caio.cAccDescr = 0; caio.rgAccDescr = NULL; pAIA->pbData = NULL; pAIA->cbData = 0; cAIA = 0; if (0 != g_caRevURL) { pTemplateEnd = &g_paCACertURL[g_caCACertURL]; for (pTemplate = g_paCACertURL; pTemplate < pTemplateEnd; pTemplate++) { if (Flags & pTemplate->Flags) { cAIA++; } } } if (0 == cAIA) { hr = S_FALSE; goto error; } caio.rgAccDescr = (CERT_ACCESS_DESCRIPTION *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cAIA * sizeof(caio.rgAccDescr[0])); if (NULL == caio.rgAccDescr) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN); for (pTemplate = g_paCACertURL; pTemplate < pTemplateEnd; pTemplate++) { if (Flags & pTemplate->Flags) { pcad = &caio.rgAccDescr[caio.cAccDescr]; pcad->pszAccessMethod = (CSURL_ADDTOCERTOCSP & pTemplate->Flags)? szOID_PKIX_OCSP : szOID_PKIX_CA_ISSUERS; pcad->AccessLocation.dwAltNameChoice = CERT_ALT_NAME_URL; hr = myFormatCertsrvStringArray( TRUE, // fURL g_pwszServerName, // pwszServerName_p1_2 g_wszSanitizedName, // pwszSanitizedName_p3_7 pCAContext->iCert, // iCert_p4 g_strDomainDN, // pwszDomainDN_p5 g_strConfigDN, // pwszConfigDN_p6 pCAContext->iKey, // iCRL_p8 FALSE, // fDeltaCRL_p9 TRUE, // fDSAttrib_p10_11 1, // cStrings (LPCWSTR *) &pTemplate->pwszURL, // apwszStringsIn &pcad->AccessLocation.pwszURL); // apwszStringsOut _JumpIfError(hr, error, "myFormatCertsrvStringArray"); caio.cAccDescr++; } } CSASSERT(caio.cAccDescr == cAIA); if (!myEncodeObject( X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS, &caio, 0, CERTLIB_USE_LOCALALLOC, &pAIA->pbData, &pAIA->cbData)) { hr = myHLastError(); _JumpIfError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (NULL != caio.rgAccDescr) { for (i = 0; i < caio.cAccDescr; i++) { pcad = &caio.rgAccDescr[i]; if (NULL != pcad->AccessLocation.pwszURL) { LocalFree(pcad->AccessLocation.pwszURL); } } LocalFree(caio.rgAccDescr); } return(hr); } // Find the newest cert with the matching key container name: HRESULT pkcsFindMatchingKeyContext( OPTIONAL IN CERT_PUBLIC_KEY_INFO *pPublicKeyInfo, IN DWORD iKey, OUT CACTX **ppCAContext) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); DWORD i; CACTX *pCAContext; *ppCAContext = NULL; for (i = g_cCACerts; i > 0; i--) { pCAContext = &g_aCAContext[i - 1]; if ((MAXDWORD != iKey && iKey == pCAContext->iKey) || (NULL != pCAContext->pccCA && NULL != pPublicKeyInfo && CertComparePublicKeyInfo( X509_ASN_ENCODING, pPublicKeyInfo, &pCAContext->pccCA->pCertInfo->SubjectPublicKeyInfo))) { // by design, CertComparePublicKeyInfo doesn't set last error! *ppCAContext = pCAContext; hr = S_OK; break; } } return(hr); } HRESULT pkcsLoadTemplates( IN WCHAR const *pwszRegName, OUT CSURLTEMPLATE **ppaURL, OUT DWORD *pcaURL) { HRESULT hr; WCHAR *pwszzTemplates = NULL; WCHAR *pwsz; DWORD cTemplate = 0; CSURLTEMPLATE *pTemplate; DWORD Flags; WCHAR *pwsz2; *ppaURL = NULL; *pcaURL = 0; // get (multiple) path templates hr = myGetCertRegMultiStrValue( g_wszSanitizedName, NULL, NULL, pwszRegName, &pwszzTemplates); _JumpIfError(hr, error, "myGetCertRegStrValue"); for (pwsz = pwszzTemplates; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1) { Flags = _wtoi(pwsz); pwsz2 = pwsz; while (iswdigit(*pwsz2)) { pwsz2++; } if (0 != Flags && pwsz2 > pwsz && L':' == *pwsz2) { cTemplate++; } } if (0 != cTemplate) { *ppaURL = (CSURLTEMPLATE *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cTemplate * sizeof((*ppaURL)[0])); if (NULL == *ppaURL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pTemplate = *ppaURL; *pcaURL = cTemplate; for (pwsz = pwszzTemplates; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1) { Flags = _wtoi(pwsz); pwsz2 = pwsz; while (iswdigit(*pwsz2)) { pwsz2++; } if (0 != Flags && pwsz2 > pwsz && L':' == *pwsz2) { pTemplate->Flags = Flags; hr = myDupString(&pwsz2[1], &pTemplate->pwszURL); _JumpIfError(hr, error, "myDupString"); pTemplate++; } } CSASSERT(pTemplate == &(*ppaURL)[*pcaURL]); } error: if (NULL != pwszzTemplates) { LocalFree(pwszzTemplates); } return(hr); } VOID pkcsFreeTemplates( IN OUT CSURLTEMPLATE **ppaURL, IN OUT DWORD *pcaURL) { CSURLTEMPLATE *pTemplate; CSURLTEMPLATE *pTemplateEnd; if (0 != *pcaURL && NULL != *ppaURL) { pTemplateEnd = &(*ppaURL)[*pcaURL]; for (pTemplate = *ppaURL; pTemplate < pTemplateEnd; pTemplate++) { if (NULL != pTemplate->pwszURL) { LocalFree(pTemplate->pwszURL); } } LocalFree(*ppaURL); *ppaURL = NULL; } } HRESULT pkcsGetCertFilename( IN WCHAR const *pwszSanitizedName, IN DWORD iCert, OUT WCHAR **ppwszCertFile) { HRESULT hr; WCHAR wszBuf[MAX_PATH]; WCHAR *pwszIndexedName = NULL; DWORD cwc; *ppwszCertFile = NULL; hr = myAllocIndexedName( pwszSanitizedName, iCert, &pwszIndexedName); _JumpIfError(hr, error, "myAllocIndexedName"); if (0 == GetEnvironmentVariable(L"SystemRoot", wszBuf, ARRAYSIZE(wszBuf))) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "GetEnvironmentVariable"); } cwc = wcslen(wszBuf) + WSZARRAYSIZE(L"\\System32\\" wszCERTENROLLSHAREPATH L"\\") + wcslen(g_pwszServerName) + WSZARRAYSIZE(L"_") + wcslen(pwszIndexedName) + WSZARRAYSIZE(L".crt") + 1; *ppwszCertFile = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == *ppwszCertFile) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } wcscpy(*ppwszCertFile, wszBuf); wcscat(*ppwszCertFile, L"\\System32\\" wszCERTENROLLSHAREPATH L"\\"); wcscat(*ppwszCertFile, g_pwszServerName); wcscat(*ppwszCertFile, L"_"); wcscat(*ppwszCertFile, pwszIndexedName); wcscat(*ppwszCertFile, L".crt"); CSASSERT(1 + wcslen(*ppwszCertFile) == cwc); error: if (NULL != pwszIndexedName) { LocalFree(pwszIndexedName); } return(hr); } HRESULT pkcsReloadMissingCertByHash( IN HCERTSTORE hStore, IN WCHAR const *pwszSanitizedName, IN DWORD dwRegHashChoice, IN BYTE const *pbHashReg, IN DWORD cbHashReg, IN DWORD iHash) { HRESULT hr; DWORD cbHash; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; BSTR strHash = NULL; ICertDBRow *prow = NULL; DWORD cbCert; BYTE *pbCert = NULL; WCHAR *pwszCertFile = NULL; CERT_CONTEXT const *pcc = NULL; hr = MultiByteIntegerToBstr(TRUE, cbHashReg, pbHashReg, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); DBGPRINT(( DBG_SS_CERTSRV, "Reloading %wsContext[%u]\n %ws\n", CSRH_CASIGCERT == dwRegHashChoice? L"CA" : L"KRA", iHash, strHash)); hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPOPEN_CERTHASH | PROPTABLE_REQCERT, 0, strHash, &prow); _PrintIfErrorStr(hr, "OpenRow", strHash); if (S_OK == hr) { hr = PKCSGetProperty( prow, g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbCert, (BYTE **) &pbCert); _JumpIfError(hr, error, "PKCSGetProperty(cert)"); } else if (CSRH_CASIGCERT != dwRegHashChoice) { _JumpError(hr, error, "OpenRow"); } else { hr = pkcsGetCertFilename( pwszSanitizedName, iHash, &pwszCertFile); _JumpIfError(hr, error, "myGetCertFilename"); hr = DecodeFileW(pwszCertFile, &pbCert, &cbCert, CRYPT_STRING_ANY); _JumpIfError(hr, error, "DecodeFileW"); } pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert); if (NULL == pcc) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } cbHash = sizeof(abHash); if (!CertGetCertificateContextProperty( pcc, CERT_SHA1_HASH_PROP_ID, abHash, &cbHash)) { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateContextProperty"); } if (cbHash != cbHashReg || 0 != memcmp(abHash, pbHashReg, cbHash)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "wrong Cert"); } // Add as encoded blob to avoid all properties, key prov info, etc. if (!CertAddEncodedCertificateToStore( hStore, X509_ASN_ENCODING, pbCert, cbCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) // ppCertContext { hr = myHLastError(); _JumpError(hr, error, "CertAddEncodedCertificateToStore"); } DBGPRINT(( DBG_SS_CERTSRV, "Reloaded %wsContext[%u]\n", CSRH_CASIGCERT == dwRegHashChoice? L"CA" : L"KRA", iHash)); hr = S_OK; error: if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (NULL != pwszCertFile) { LocalFree(pwszCertFile); } if (NULL != prow) { prow->Release(); } if (NULL != pbCert) { LocalFree(pbCert); } if (NULL != strHash) { SysFreeString(strHash); } return(hr); } HRESULT pkcsReloadMissingCAOrKRACert( IN HCERTSTORE hStore, IN WCHAR const *pwszSanitizedName, IN DWORD dwRegHashChoice, IN DWORD iHash) { HRESULT hr; BYTE *pbHashReg = NULL; DWORD cbHashReg; hr = myGetCARegHash( pwszSanitizedName, dwRegHashChoice, iHash, &pbHashReg, &cbHashReg); _JumpIfError(hr, error, "myGetCARegHash"); hr = pkcsReloadMissingCertByHash( hStore, pwszSanitizedName, dwRegHashChoice, pbHashReg, cbHashReg, iHash); _JumpIfError(hr, error, "pkcsReloadMissingCertByHash"); error: if (NULL != pbHashReg) { LocalFree(pbHashReg); } return(hr); } VOID pkcsFreeBlobArray( IN DWORD cBlob, CERT_BLOB *rgBlob) { DWORD i; for (i = 0; i < cBlob; i++) { if (NULL != rgBlob[cBlob].pbData) { LocalFree(rgBlob[i].pbData); } } LocalFree(rgBlob); } HRESULT pkcsGetKRACertBlobs( IN ICertDBRow *prow, OUT DWORD *pcCertBlob, OUT CERT_BLOB **prgCertBlob) { HRESULT hr; WCHAR *pwszHashes = NULL; WCHAR *pwsz; DWORD cb; DWORD cHash; DWORD i; CERT_BLOB *rgBlob = NULL; HCERTSTORE hStore = NULL; CRYPT_DATA_BLOB HashBlob; DWORD cBlobLoaded; CERT_CONTEXT const *pcc = NULL; HashBlob.pbData = NULL; hr = PKCSGetProperty( prow, g_wszPropRequestKeyRecoveryHashes, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE **) &pwszHashes); _JumpIfError(hr, error, "PKCSGetProperty(KRA hashes)"); cHash = 1; pwsz = pwszHashes; while (TRUE) { pwsz = wcschr(pwsz, L'\n'); if (NULL == pwsz) { break; } *pwsz++ = L'\0'; cHash++; } cBlobLoaded = 0; rgBlob = (CERT_BLOB *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cHash * sizeof(rgBlob[0])); if (NULL == rgBlob) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } // open KRA store hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE, wszKRA_CERTSTORE); if (NULL == hStore) { hr = myHLastError(); _JumpError(hr, error, "CertOpenStore"); } pwsz = pwszHashes; for (i = 0; i < cHash; i++) { BOOL fReloaded; hr = WszToMultiByteInteger( TRUE, pwsz, &HashBlob.cbData, &HashBlob.pbData); _JumpIfError(hr, error, "WszToMultiByteInteger"); fReloaded = FALSE; while (TRUE) { pcc = CertFindCertificateInStore( hStore, X509_ASN_ENCODING, 0, CERT_FIND_HASH, &HashBlob, NULL); if (fReloaded || NULL != pcc) { break; } hr = pkcsReloadMissingCertByHash( hStore, g_wszSanitizedName, CSRH_CAKRACERT, HashBlob.pbData, HashBlob.cbData, i); _PrintIfError(hr, "pkcsReloadMissingCertByHash"); fReloaded = TRUE; } if (NULL == pcc) { hr = myHLastError(); _PrintError(hr, "CertFindCertificateInStore"); } else { rgBlob[cBlobLoaded].pbData = (BYTE *) LocalAlloc( LMEM_FIXED, pcc->cbCertEncoded); if (NULL == rgBlob[cBlobLoaded].pbData) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } rgBlob[cBlobLoaded].cbData = pcc->cbCertEncoded; CopyMemory( rgBlob[cBlobLoaded].pbData, pcc->pbCertEncoded, pcc->cbCertEncoded); cBlobLoaded++; CertFreeCertificateContext(pcc); pcc = NULL; } pwsz += wcslen(pwsz) + 1; LocalFree(HashBlob.pbData); HashBlob.pbData = NULL; } *pcCertBlob = cBlobLoaded; *prgCertBlob = rgBlob; rgBlob = NULL; error: if (NULL != rgBlob) { pkcsFreeBlobArray(cBlobLoaded, rgBlob); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (NULL != HashBlob.pbData) { LocalFree(HashBlob.pbData); } if (NULL != pwszHashes) { LocalFree(pwszHashes); } return(hr); } HRESULT pkcsGetHashAsOctet( IN ICertDBRow *prow, OUT BYTE **ppbData, OUT DWORD *pcbData) { HRESULT hr; WCHAR *pwszHash = NULL; DWORD cb; CRYPT_DATA_BLOB Blob; DWORD cbHash; *ppbData = NULL; Blob.pbData = NULL; hr = PKCSGetProperty( prow, g_wszPropCertificateHash, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cb, (BYTE **) &pwszHash); _JumpIfError(hr, error, "PKCSGetProperty(hash)"); hr = WszToMultiByteInteger(TRUE, pwszHash, &Blob.cbData, &Blob.pbData); _JumpIfError(hr, error, "WszToMultiByteInteger"); if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &Blob, 0, CERTLIB_USE_LOCALALLOC, ppbData, pcbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (NULL != pwszHash) { LocalFree(pwszHash); } if (NULL != Blob.pbData) { LocalFree(Blob.pbData); } return(hr); } VOID AddCertBlobToArray( IN CERT_CONTEXT const *pcc, IN CERT_BLOB *rgCertBlobAll, IN CERT_BLOB **ppCertBlob) { CERT_BLOB *pCertBlob = *ppCertBlob; DWORD i; DWORD cBlob; cBlob = SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll); for (i = 0; i < cBlob; i++) { if (rgCertBlobAll[i].cbData == pcc->cbCertEncoded && 0 == memcmp( rgCertBlobAll[i].pbData, pcc->pbCertEncoded, pcc->cbCertEncoded)) { DBGPRINT(( DBG_SS_CERTSRV, "Duplicate Recovery Blob Cert[%u]\n", i)); goto error; } } DBGPRINT(( DBG_SS_CERTSRV, "Adding Recovery Blob Cert[%u]\n", cBlob)); pCertBlob->cbData = pcc->cbCertEncoded; pCertBlob->pbData = pcc->pbCertEncoded; pCertBlob++; *ppCertBlob = pCertBlob; error: ; } HRESULT PKCSGetArchivedKey( IN DWORD dwRequestId, OUT BYTE **ppbArchivedKey, // CoTaskMem* OUT DWORD *pcbArchivedKey) { HRESULT hr; ICertDBRow *prow = NULL; BYTE *pbKey = NULL; DWORD cbKey; BYTE *pbCertUser = NULL; DWORD cbCertUser; DWORD cb; HCRYPTMSG hMsg = NULL; CACTX *pCAContext; CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo; CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo; CERT_CONTEXT const *pccUser = NULL; CERT_CHAIN_CONTEXT const *pCertChainContextUser = NULL; CERT_CHAIN_PARA CertChainPara; CERT_BLOB *rgCertBlobKRA = NULL; DWORD cCertBlobKRA; CERT_BLOB *rgCertBlobAll = NULL; DWORD cCertBlobAll; CERT_BLOB *pCertBlob; CRYPT_ATTRIBUTE HashAttrib; CRYPT_ATTR_BLOB HashAttribBlob; DWORD i; *ppbArchivedKey = NULL; HashAttribBlob.pbData = NULL; hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPTABLE_REQCERT, dwRequestId, NULL, &prow); _JumpIfError(hr, error, "OpenRow"); hr = PKCSGetProperty( prow, g_wszPropRequestRawArchivedKey, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbKey, &pbKey); _JumpIfError(hr, error, "PKCSGetProperty(key)"); hr = pkcsGetKRACertBlobs(prow, &cCertBlobKRA, &rgCertBlobKRA); _JumpIfError(hr, error, "pkcsGetKRACertBlobs"); hr = pkcsGetHashAsOctet( prow, &HashAttribBlob.pbData, &HashAttribBlob.cbData); _JumpIfError(hr, error, "pkcsGetHashAsOctet"); HashAttrib.pszObjId = szOID_ARCHIVED_KEY_CERT_HASH; HashAttrib.cValue = 1; HashAttrib.rgValue = &HashAttribBlob; hr = PKCSGetProperty( prow, g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbCertUser, &pbCertUser); _JumpIfError(hr, error, "PKCSGetProperty(cert)"); pccUser = CertCreateCertificateContext( X509_ASN_ENCODING, pbCertUser, cbCertUser); if (NULL == pccUser) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } // build the user cert chain ZeroMemory(&CertChainPara, sizeof(CertChainPara)); CertChainPara.cbSize = sizeof(CertChainPara); if (!CertGetCertificateChain( HCCE_LOCAL_MACHINE, pccUser, NULL, NULL, &CertChainPara, 0, NULL, &pCertChainContextUser)) { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateChain"); } // make sure there is at least 1 simple chain if (0 == pCertChainContextUser->cChain) { hr = E_INVALIDARG; _JumpError(hr, error, "No user chain"); } // Encode the encrypted key into a PKCS 7, signed by the current CA cert. // Initialize the CMSG_SIGNER_ENCODE_INFO structure for one signer. pCAContext = g_pCAContextCurrent; cCertBlobAll = cCertBlobKRA + pCAContext->cCACertChain + pCertChainContextUser->rgpChain[0]->cElement; rgCertBlobAll = (CERT_BLOB *) LocalAlloc( LMEM_FIXED, cCertBlobAll * sizeof(rgCertBlobAll[0])); if (NULL == rgCertBlobAll) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pCertBlob = rgCertBlobAll; CopyMemory(pCertBlob, rgCertBlobKRA, cCertBlobKRA * sizeof(pCertBlob[0])); pCertBlob += cCertBlobKRA; // Add the current CA cert chain for (i = 0; i < pCAContext->cCACertChain; i++) { AddCertBlobToArray( pCAContext->apCACertChain[i], rgCertBlobAll, &pCertBlob); } // Add the user cert chain { CERT_SIMPLE_CHAIN *pSimpleChain; pSimpleChain = pCertChainContextUser->rgpChain[0]; for (i = 0; i < pSimpleChain->cElement; i++) { AddCertBlobToArray( pSimpleChain->rgpElement[i]->pCertContext, rgCertBlobAll, &pCertBlob); } } CSASSERT(pCertBlob <= &rgCertBlobAll[cCertBlobAll]); DBGPRINT(( DBG_SS_CERTSRV, "Recovery Certs: %u --> %u\n", cCertBlobAll, SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll))); cCertBlobAll = SAFE_SUBTRACT_POINTERS(pCertBlob, rgCertBlobAll); ZeroMemory(&SignerEncodeInfo, sizeof(SignerEncodeInfo)); SignerEncodeInfo.cbSize = sizeof(SignerEncodeInfo); SignerEncodeInfo.pCertInfo = pCAContext->pccCA->pCertInfo; SignerEncodeInfo.hCryptProv = pCAContext->hProvCA; SignerEncodeInfo.dwKeySpec = AT_SIGNATURE; SignerEncodeInfo.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1; SignerEncodeInfo.cAuthAttr = 1; SignerEncodeInfo.rgAuthAttr = &HashAttrib; //SignerEncodeInfo.cUnauthAttr = 0; //SignerEncodeInfo.rgUnauthAttr = NULL; //SignerEncodeInfo.HashEncryptionAlgorithm.pszObjId = ???; // CERT_ID_SHA1_HASH is not yet implemented in CryptMsgOpenToEncode //SignerEncodeInfo.SignerId.dwIdChoice = CERT_ID_SHA1_HASH; //SignerEncodeInfo.SignerId.HashId.cbData = cb; //SignerEncodeInfo.SignerId.HashId.pbData = abHash; ZeroMemory(&SignedMsgEncodeInfo, sizeof(SignedMsgEncodeInfo)); SignedMsgEncodeInfo.cbSize = sizeof(SignedMsgEncodeInfo); SignedMsgEncodeInfo.cSigners = 1; SignedMsgEncodeInfo.rgSigners = &SignerEncodeInfo; SignedMsgEncodeInfo.cCertEncoded = cCertBlobAll; SignedMsgEncodeInfo.rgCertEncoded = rgCertBlobAll; //SignedMsgEncodeInfo.cCrlEncoded = 0; //SignedMsgEncodeInfo.rgCrlEncoded = NULL; hMsg = CryptMsgOpenToEncode( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, // dwFlags CMSG_SIGNED, // dwMsgType &SignedMsgEncodeInfo, // pvMsgEncodeInfo NULL, // pszInnerContentObjID NULL); // pStreamInfo if (NULL == hMsg) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgOpenToDecode"); } if (!CryptMsgUpdate(hMsg, pbKey, cbKey, TRUE)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgUpdate"); } // Return the encoded and signed content. // Use CMSG_CONTENT_PARAM to get the signed message. hr = myCryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, CERTLIB_USE_COTASKMEMALLOC, (VOID **) ppbArchivedKey, pcbArchivedKey); _JumpIfError(hr, error, "myCryptMsgGetParam"); error: if (pCertChainContextUser != NULL) { CertFreeCertificateChain(pCertChainContextUser); } if (NULL != pccUser) { CertFreeCertificateContext(pccUser); } if (NULL != hMsg) { CryptMsgClose(hMsg); } if (NULL != rgCertBlobKRA) { pkcsFreeBlobArray(cCertBlobKRA, rgCertBlobKRA); } if (NULL != rgCertBlobAll) { LocalFree(rgCertBlobAll); } if (NULL != HashAttribBlob.pbData) { LocalFree(HashAttribBlob.pbData); } if (NULL != pbKey) { LocalFree(pbKey); } if (NULL != pbCertUser) { LocalFree(pbCertUser); } if (NULL != prow) { prow->Release(); } return(hr); } HRESULT pkcsGetKeyContainerName( IN CERT_CONTEXT const *pccCA, OUT WCHAR **ppwszKeyContainerName) { HRESULT hr; CRYPT_HASH_BLOB KeyIdentifier; CRYPT_KEY_PROV_INFO *pkpi = NULL; DWORD cb; KeyIdentifier.pbData = NULL; *ppwszKeyContainerName = NULL; hr = myGetPublicKeyHash( pccCA->pCertInfo, &pccCA->pCertInfo->SubjectPublicKeyInfo, &KeyIdentifier.pbData, &KeyIdentifier.cbData); _JumpIfError(hr, error, "myGetPublicKeyHash"); cb = 0; while (TRUE) { if (!CryptGetKeyIdentifierProperty( &KeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_MACHINE_FLAG, NULL, // pwszComputerName NULL, // pvReserved pkpi, &cb)) { hr = myHLastError(); _JumpError(hr, error, "Cert index"); } if (NULL != pkpi) { break; } pkpi = (CRYPT_KEY_PROV_INFO *) LocalAlloc(LMEM_FIXED, cb); if (NULL == pkpi) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } } hr = myDupString(pkpi->pwszContainerName, ppwszKeyContainerName); _JumpIfError(hr, error, "myDupString"); error: if (NULL != pkpi) { LocalFree(pkpi); } if (NULL != KeyIdentifier.pbData) { LocalFree(KeyIdentifier.pbData); } return(hr); } HRESULT pkcsLoadCAContext( IN WCHAR const *pwszSanitizedName, IN WCHAR *pwszProvName, IN DWORD dwProvType, IN ALG_ID idAlg, IN BOOL fMachineKeyset, IN DWORD iHash, IN HCERTSTORE hMyStore) { HRESULT hr; HCRYPTPROV hProvCA = NULL; char *pszObjIdSignatureAlgorithm = NULL; WCHAR *pwszKeyContainerName = NULL; CERT_CONTEXT const *pccCA = NULL; DWORD cCACertChain; CERT_CONTEXT const **apCACertChain = NULL; CERT_CHAIN_CONTEXT const *pCertChainContext = NULL; CERT_CHAIN_PARA CertChainPara; CRYPT_KEY_PROV_INFO *pKey = NULL; CACTX *pCAContext; DWORD i; DWORD cbKey; DWORD iCert; DWORD iKey; DWORD NameId; BOOL fReloaded; hr = myGetSigningOID( NULL, // hProv pwszProvName, dwProvType, idAlg, &pszObjIdSignatureAlgorithm); _JumpIfError(hr, error, "myGetSigningOID"); if (~_16BITMASK & iHash) { hr = E_INVALIDARG; _JumpError(hr, error, "Cert index"); } fReloaded = FALSE; while (TRUE) { hr = myFindCACertByHashIndex( hMyStore, pwszSanitizedName, CSRH_CASIGCERT, iHash, &NameId, &pccCA); iCert = iHash; iKey = iCert; if (S_OK == hr) { break; } // if no hash entry exists for this index, fake up a CA Context // as a place holder. if (S_FALSE == hr) { CSASSERT(MAXDWORD == NameId); CSASSERT(NULL == pccCA); break; } if (fReloaded || CRYPT_E_NOT_FOUND != hr) { _JumpError(hr, error, "myFindCACertByHashIndex"); } _PrintError(hr, "myFindCACertByHashIndex"); // The CA cert is missing from the HKLM "my" store -- look it up in // the DB or the CertEnroll directory, and put it back in the store. hr = pkcsReloadMissingCAOrKRACert( hMyStore, pwszSanitizedName, CSRH_CASIGCERT, iHash); _JumpIfError(hr, error, "pkcsReloadMissingCAOrKRACert"); fReloaded = TRUE; } CSASSERT(S_FALSE == hr || S_OK == hr); if (S_OK == hr) { if (MAXDWORD != NameId && iCert != CANAMEIDTOICERT(NameId)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); DBGPRINT(( DBG_SS_CERTSRV, "NameId=%u.%u iCert=%u\n", CANAMEIDTOICERT(NameId), CANAMEIDTOIKEY(NameId), iCert)); _JumpError(hr, error, "bad iCert"); } fReloaded = FALSE; while (TRUE) { if (NULL != pwszKeyContainerName) { LocalFree(pwszKeyContainerName); pwszKeyContainerName = NULL; } if (!fReloaded) { // get the private key provider info if (!myCertGetCertificateContextProperty( pccCA, CERT_KEY_PROV_INFO_PROP_ID, CERTLIB_USE_LOCALALLOC, (VOID **) &pKey, &cbKey)) { hr = myHLastError(); if (CRYPT_E_NOT_FOUND != hr) { _JumpError(hr, error, "myCertGetCertificateContextProperty"); } _PrintError(hr, "CertGetCertificateContextProperty"); // The Key Provider Info is missing -- use the sanitized // name and key index to construct the key container name. // If that key matches, we'll write out the new Key // Provider Info below. hr = myAllocIndexedName( pwszSanitizedName, MAXDWORD != NameId? CANAMEIDTOIKEY(NameId) : iCert, &pwszKeyContainerName); _JumpIfError(hr, error, "myAllocIndexedName"); } else { hr = myDupString(pKey->pwszContainerName, &pwszKeyContainerName); _JumpIfError(hr, error, "myDupString"); } } else { hr = pkcsGetKeyContainerName(pccCA, &pwszKeyContainerName); _JumpIfError(hr, error, "pkcsGetKeyContainerName"); } // signing testing hr = myValidateHashForSigning( pwszKeyContainerName, pwszProvName, dwProvType, fMachineKeyset, &pccCA->pCertInfo->SubjectPublicKeyInfo, idAlg); if (S_OK == hr) { break; } if (fReloaded) { _JumpError(hr, error, "myValidateHashForSigning"); } _PrintError(hr, "myValidateHashForSigning"); fReloaded = TRUE; } // If the Key Provider Info is missing, write out new Key Provider Info if (NULL == pKey) { CRYPT_KEY_PROV_INFO kpi; ZeroMemory(&kpi, sizeof(kpi)); kpi.pwszContainerName = pwszKeyContainerName; kpi.pwszProvName = pwszProvName; kpi.dwProvType = dwProvType; kpi.dwFlags = fMachineKeyset? CRYPT_MACHINE_KEYSET : 0; kpi.dwKeySpec = AT_SIGNATURE; if (!CertSetCertificateContextProperty( pccCA, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi)) { hr = myHLastError(); _JumpError(hr, error, "CertSetCertificateContextProperty"); } DBGPRINT(( DBG_SS_CERTSRV, "Reloaded CAContext[%u] KeyProvInfo[%u]\n", iCert, iKey)); } hr = pkcsFindMatchingKeyContext( &pccCA->pCertInfo->SubjectPublicKeyInfo, MAXDWORD, &pCAContext); if (S_OK != hr && MAXDWORD != NameId) { iKey = CANAMEIDTOIKEY(NameId); if (iKey < iCert) { hr = pkcsFindMatchingKeyContext(NULL, iKey, &pCAContext); _JumpIfError(hr, error, "pkcsFindMatchingKeyContext"); } } if (S_OK == hr) { iKey = pCAContext->iKey; if (MAXDWORD != NameId && iKey != CANAMEIDTOIKEY(NameId)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "bad iKey"); } if (NULL == pCAContext->pccCA) { CSASSERT(pCAContext->Flags & CTXF_CERTMISSING); pCAContext->Flags |= CTXF_CRLZOMBIE; } else { CSASSERT(0 == (pCAContext->Flags & CTXF_CERTMISSING)); pCAContext->Flags |= CTXF_SKIPCRL; } } else { g_cCAKeys++; // this key has not previously been loaded } DBGPRINT(( DBG_SS_CERTSRV, "CAContext[%u]: Key %u: %ws\n", iCert, iKey, pwszKeyContainerName)); // get private key handler for later use if current CA if (!myCertSrvCryptAcquireContext( &hProvCA, pwszKeyContainerName, pwszProvName, dwProvType, g_fCryptSilent? CRYPT_SILENT : 0, fMachineKeyset)) { hr = myHLastError(); _JumpErrorStr( hr, error, "myCertSrvCryptAcquireContext", pwszKeyContainerName); } // now try to figure out the chain ZeroMemory(&CertChainPara, sizeof(CertChainPara)); CertChainPara.cbSize = sizeof(CertChainPara); if (!CertGetCertificateChain( HCCE_LOCAL_MACHINE, pccCA, NULL, NULL, &CertChainPara, 0, NULL, &pCertChainContext)) { hr = myHLastError(); goto error; } // make sure there is at least 1 simple chain if (pCertChainContext->cChain == 0) { hr = E_INVALIDARG; _JumpError(hr, error, "No valid trust chain could be formed"); } // tell global how many elements we have in our chain cCACertChain = pCertChainContext->rgpChain[0]->cElement; // Allocate memory for global. Allocate one extra pointer to allow loop // to assign NULL pointer in place in array. Leave the count set to the // actual number of CA cert contexts, excluding the NULL pointer. apCACertChain = (CERT_CONTEXT const **) LocalAlloc( LMEM_FIXED, (cCACertChain + 1) * sizeof(apCACertChain[0])); if (NULL == apCACertChain) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } // copy chain in reverse order: from parent to child { int i; for (i = cCACertChain - 1; i >= 0; i--) { apCACertChain[i] = CertDuplicateCertificateContext( pCertChainContext->rgpChain[0]->rgpElement[i]->pCertContext); if (NULL == apCACertChain[i]) { hr = myHLastError(); _JumpError(hr, error, "CertDuplicateCertificateContext"); } } } } for (i = 0; i < g_cCACerts; i++) { if (iCert < g_aCAContext[i].iCert) { MoveMemory( &g_aCAContext[i + 1], &g_aCAContext[i], (g_cCACerts - i) * sizeof(g_aCAContext[0])); break; } } g_cCACerts++; pCAContext = &g_aCAContext[i]; ZeroMemory(pCAContext, sizeof(*pCAContext)); if (NULL == pccCA) { pCAContext->Flags |= CTXF_CERTMISSING | CTXF_SKIPCRL; } pCAContext->iCert = iCert; pCAContext->iKey = iKey; pCAContext->NameId = MAKECANAMEID(iCert, iKey); pCAContext->hProvCA = hProvCA; hProvCA = NULL; pCAContext->pccCA = pccCA; pccCA = NULL; if (NULL != apCACertChain) { pCAContext->cCACertChain = cCACertChain; pCAContext->apCACertChain = apCACertChain; apCACertChain = NULL; } pCAContext->pszObjIdSignatureAlgorithm = pszObjIdSignatureAlgorithm; pszObjIdSignatureAlgorithm = NULL; pCAContext->pwszKeyContainerName = pwszKeyContainerName; pwszKeyContainerName = NULL; // Ignore failure from here on -- collected data is optional if (NULL != pCAContext->pccCA) { hr = myGetPublicKeyHash( pCAContext->pccCA->pCertInfo, &pCAContext->pccCA->pCertInfo->SubjectPublicKeyInfo, &pCAContext->IssuerKeyId.pbData, &pCAContext->IssuerKeyId.cbData); _PrintIfError(hr, "myGetPublicKeyHash"); if (0 == (CTXF_SKIPCRL & pCAContext->Flags)) { hr = pkcsBuildKeyAuthority2( g_CRLEditFlags, pCAContext, &pCAContext->KeyAuthority2CRL); _PrintIfError(hr, "pkcsBuildKeyAuthority2"); hr = pkcsBuildCDP( CSURL_ADDTOFRESHESTCRL, TRUE, pCAContext, &pCAContext->CDPCRLFreshest); _PrintIfError(hr, "pkcsBuildCDP"); hr = pkcsBuildCDP( CSURL_ADDTOCRLCDP, FALSE, pCAContext, &pCAContext->CDPCRLBase); _PrintIfError(hr, "pkcsBuildCDP"); hr = pkcsBuildCDP( CSURL_ADDTOCRLCDP, TRUE, pCAContext, &pCAContext->CDPCRLDelta); _PrintIfError(hr, "pkcsBuildCDP"); hr = pkcsBuildCRLList( FALSE, pCAContext, &pCAContext->papwszCRLFiles); _JumpIfError(hr, error, "pkcsBuildCRLList"); hr = pkcsBuildCRLList( TRUE, pCAContext, &pCAContext->papwszDeltaCRLFiles); _JumpIfError(hr, error, "pkcsBuildCRLList"); } } hr = S_OK; error: if (NULL != hProvCA) { CryptReleaseContext(hProvCA, 0); } if (NULL != pszObjIdSignatureAlgorithm) { LocalFree(pszObjIdSignatureAlgorithm); } if (NULL != pwszKeyContainerName) { LocalFree(pwszKeyContainerName); } if (NULL != pKey) { LocalFree(pKey); } if (pCertChainContext != NULL) { CertFreeCertificateChain(pCertChainContext); } if (NULL != pccCA) { CertFreeCertificateContext(pccCA); } return(hr); } HRESULT pkcsLoadCAContextArray( IN WCHAR const *pwszCommonName, IN WCHAR const *pwszSanitizedName) { HRESULT hr; DWORD cCACerts; HCERTSTORE hMyStore = NULL; WCHAR *pwszProvName = NULL; DWORD dwProvType; ALG_ID idAlg; BOOL fMachineKeyset; DWORD iHash; // get provider name hr = myGetCertSrvCSP( FALSE, // fEncryptionCSP pwszSanitizedName, &dwProvType, &pwszProvName, &idAlg, &fMachineKeyset, NULL); // pdwKeySize _JumpIfError(hr, error, "myGetCertSrvCSP"); // open MY store hMyStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE, wszMY_CERTSTORE); if (NULL == hMyStore) { hr = myHLastError(); _JumpError(hr, error, "CertOpenStore"); } // find & load CA certs, etc. hr = myGetCARegHashCount(pwszSanitizedName, CSRH_CASIGCERT, &cCACerts); if (S_OK == hr && 0 == cCACerts) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } _JumpIfError(hr, error, "myGetCARegHashCount"); g_aCAContext = (CACTX *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cCACerts * sizeof(g_aCAContext[0])); if (NULL == g_aCAContext) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } for (iHash = 0; iHash < cCACerts; iHash++) { hr = pkcsLoadCAContext( pwszSanitizedName, pwszProvName, dwProvType, idAlg, fMachineKeyset, iHash, hMyStore); if (S_FALSE == hr) { continue; } _JumpIfError(hr, error, "pkcsLoadCAContext"); } g_pCAContextCurrent = &g_aCAContext[g_cCACerts - 1]; // Only build a Key Authority extension for the current CACTX -- it's the // only one used to issue certs. hr = pkcsBuildKeyAuthority2( EDITF_ENABLEAKIKEYID | EDITF_ENABLEAKIISSUERNAME | EDITF_ENABLEAKIISSUERSERIAL, g_pCAContextCurrent, &g_pCAContextCurrent->KeyAuthority2Cert); _PrintIfError(hr, "pkcsBuildKeyAuthority2"); // Only build a CDP extension for the current CACTX -- it's the // only one used to issue certs. hr = pkcsBuildCDP( CSURL_ADDTOCERTCDP, FALSE, g_pCAContextCurrent, &g_pCAContextCurrent->CDPCert); _PrintIfError(hr, "pkcsBuildCDP"); // Only build a CDP extension for the current CACTX -- it's the // only one used to issue certs. hr = pkcsBuildAIA( CSURL_ADDTOCERTCDP | CSURL_ADDTOCERTOCSP, g_pCAContextCurrent, &g_pCAContextCurrent->AIACert); _PrintIfError(hr, "pkcsBuildAIA"); hr = S_OK; error: if (NULL != pwszProvName) { LocalFree(pwszProvName); } if (NULL != hMyStore) { CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG); } return(hr); } HRESULT pkcsImportCAOrKRACert( IN CERT_CONTEXT const *pcc, IN DWORD DBDisposition, OPTIONAL IN CACTX const *pCAContext) { HRESULT hr; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; DWORD cbHash; BSTR strHash = NULL; ICertDBRow *prow = NULL; WCHAR *pwszUserName = NULL; DWORD cb; BOOL fCommit = FALSE; BOOL fCommitted = FALSE; cbHash = sizeof(abHash); if (!CertGetCertificateContextProperty( pcc, CERT_HASH_PROP_ID, abHash, &cbHash)) { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateContextProperty"); } hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); // Import Cert if it doesn't already exist in DB: hr = g_pCertDB->OpenRow( PROPOPEN_CERTHASH | PROPTABLE_REQCERT, 0, strHash, &prow); if (S_OK != hr) { CSASSERT(CERTSRV_E_PROPERTY_EMPTY == hr); hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, &prow); _JumpIfError(hr, error, "OpenRow"); hr = PKCSParseImportedCertificate( DBDisposition, prow, pCAContext, pcc); _JumpIfError(hr, error, "PKCSParseImportedCertificate"); fCommit = TRUE; } // Set requester name if missing hr = prow->GetProperty( g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, NULL); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = myGetComputerObjectName(NameSamCompatible, &pwszUserName); if (S_OK != hr) { _PrintError(hr, "myGetComputerObjectName"); hr = myGetUserNameEx(NameSamCompatible, &pwszUserName); _JumpIfError(hr, error, "myGetUserNameEx"); } hr = prow->SetProperty( g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszUserName); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( g_wszPropCallerName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszUserName); _JumpIfError(hr, error, "SetProperty"); fCommit = TRUE; } hr = prow->CommitTransaction(fCommit); _JumpIfError(hr, error, "CommitTransaction"); fCommitted = TRUE; hr = S_OK; error: if (NULL != prow) { if (S_OK != hr && !fCommitted) { HRESULT hr2 = prow->CommitTransaction(FALSE); _PrintIfError(hr2, "CommitTransaction"); } prow->Release(); } if (NULL != pwszUserName) { LocalFree(pwszUserName); } if (NULL != strHash) { SysFreeString(strHash); } return(hr); } HRESULT pkcsImportCAContext( IN CACTX const *pCAContext) { HRESULT hr; HRESULT hr2; DWORD i; hr = S_OK; for (i = 0; i < pCAContext->cCACertChain; i++) { CERT_CONTEXT const *pCert = pCAContext->apCACertChain[i]; hr2 = pkcsImportCAOrKRACert( pCert, 0 == i? DB_DISP_CA_CERT : DB_DISP_CA_CERT_CHAIN, pCAContext); if (S_OK != hr2) { if (S_OK == hr) { hr = hr2; // return first error } _PrintError(hr2, "pkcsImportCAOrKRACert"); continue; } } _JumpIfError(hr, error, "pkcsImportCAOrKRACert"); error: return(hr); } HRESULT pkcsImportCAContextArray() { HRESULT hr = S_OK; HRESULT hr2; DWORD i; for (i = 0; i < g_cCACerts; i++) { CACTX *pCAContext = &g_aCAContext[i]; if (NULL == pCAContext->pccCA) { continue; } hr2 = pkcsImportCAContext(pCAContext); if (S_OK != hr2) { _PrintError(hr2, "pkcsImportCAContext"); if (S_OK == hr) { hr = hr2; // return first error } } } //error: return(hr); } PCCERT_CONTEXT pkcsFindCertificateInOtherStore( 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, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, // dwCertEncodingType 0, // dwFindFlags CERT_FIND_SHA1_HASH, (const void *) &HashBlob, NULL //pPrevCertContext ); } HRESULT pkcsObtainNTAuthStore(IN HCERTSTORE *phCertStore) { HRESULT hr=E_INVALIDARG; HCERTSTORE hEnterpriseStore = NULL; LPWSTR pwszLdapStore=NULL; if((NULL==phCertStore) || (NULL==g_strConfigDN)) _JumpError(hr, error, "LocalAlloc for pwazLdapStore"); *phCertStore=NULL; pwszLdapStore = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(g_strConfigDN)+wcslen(g_wszNTAuth))); if(pwszLdapStore == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc for pwazLdapStore"); } wsprintf(pwszLdapStore, g_wszNTAuth, g_strConfigDN); hEnterpriseStore = CertOpenStore(CERT_STORE_PROV_LDAP, 0, 0, CERT_STORE_READONLY_FLAG | CERT_LDAP_STORE_SIGN_FLAG, pwszLdapStore); if(NULL == hEnterpriseStore) { hr=myHLastError(); _JumpError(hr, error, "CertOpenStore"); } *phCertStore=hEnterpriseStore; hr = S_OK; error: if(pwszLdapStore) LocalFree(pwszLdapStore); return hr; } HRESULT pkcsVerifySignatureCertContext( IN WCHAR const * pwszCommonName, IN HCERTSTORE hNTAuthStore, IN DWORD dwCAIndex, IN OUT CACTX *pCAContext, OUT DWORD *pLogMsg, OUT BOOL *pfWarn) { HRESULT hr; WCHAR awc[11]; WCHAR const *apwsz[2]; PCCERT_CONTEXT pCertContext = NULL; *pLogMsg = 0; *pfWarn = FALSE; hr = myVerifyCertContext( pCAContext->pccCA, // pCert 0, // dwFlags 0, // cUsageOids NULL, // apszUsageOids HCCE_LOCAL_MACHINE, // hChainEngine NULL, // hAdditionalStore NULL); // ppwszMissingIssuer pCAContext->hrVerifyStatus = hr; if (S_OK != hr) { _PrintError2(hr, "myVerifyCertContext", CRYPT_E_REVOCATION_OFFLINE); if (CERT_E_EXPIRED == hr) { pCAContext->Flags |= CTXF_EXPIRED; if (0 == (CRLF_PUBLISH_EXPIRED_CERT_CRLS & g_dwCRLFlags)) { pCAContext->Flags |= CTXF_SKIPCRL; } *pLogMsg = MSG_E_CA_CERT_EXPIRED; } else if (CRYPT_E_REVOKED == hr) { pCAContext->Flags |= CTXF_REVOKED | CTXF_SKIPCRL; *pLogMsg = MSG_E_CA_CERT_REVOKED; } else if (CRYPT_E_REVOCATION_OFFLINE == hr) { DWORD dwState; hr = GetSetupStatus(NULL, &dwState); if ((S_OK != hr || 0 == (SETUP_CREATEDB_FLAG & dwState)) && CERTLOG_WARNING <= g_dwLogLevel) { *pLogMsg = MSG_E_CA_CERT_REVOCATION_OFFLINE; *pfWarn = TRUE; } else { hr = S_OK; } } else if (CRYPT_E_NO_REVOCATION_CHECK == hr) { if (CERTLOG_VERBOSE <= g_dwLogLevel) { *pLogMsg = MSG_E_CA_CERT_REVOCATION_NOT_CHECKED; *pfWarn = TRUE; } else { hr = S_OK; } } else { *pLogMsg = MSG_E_CA_CHAIN; } _JumpIfError(hr, error, "myVerifyCertContext"); } //the CA's certificate looks good. We verify the CA's //certificate is in the NTAuth store if(hNTAuthStore) { if(NULL == (pCertContext=pkcsFindCertificateInOtherStore( hNTAuthStore, pCAContext->pccCA))) { wsprintf(awc, L"%u", dwCAIndex); apwsz[0] = awc; apwsz[1] = pwszCommonName; LogEvent(EVENTLOG_WARNING_TYPE, MSG_CA_CERT_NO_IN_AUTH, ARRAYSIZE(apwsz), apwsz); } if(pCertContext) CertFreeCertificateContext(pCertContext); } error: return(hr); } HRESULT pkcsVerifySignatureCertContextArray( IN WCHAR const * pwszCommonName, OUT DWORD *pLogMsg, OUT BOOL *pfWarn) { HRESULT hr; DWORD i; WCHAR const *apwsz[1]; HCERTSTORE hNTAuthStore=NULL; CSASSERT(0 != g_cCACerts); //we need to verify CA's certificates should be in //the NTAuth store if the certificate is not yet expired or revoked if(IsEnterpriseCA(g_CAType)) { pkcsObtainNTAuthStore(&hNTAuthStore); if(NULL == hNTAuthStore) { apwsz[0] = pwszCommonName; LogEvent(EVENTLOG_WARNING_TYPE, MSG_CA_CERT_NO_AUTH_STORE, ARRAYSIZE(apwsz), apwsz); } } for (i = 0; i < g_cCACerts; i++) { CACTX *pCAContext = &g_aCAContext[i]; if (NULL == pCAContext->pccCA) { continue; } // Ignore all errors except for the current CA (last entry in array) hr = pkcsVerifySignatureCertContext( pwszCommonName, hNTAuthStore, i, pCAContext, pLogMsg, pfWarn); } if(hNTAuthStore) CertCloseStore(hNTAuthStore, 0); return(hr); } VOID PKCSVerifyCAState( IN OUT CACTX *pCAContext) { HRESULT hr; if (0 == pCAContext->Flags && NULL != pCAContext->pccCA) { DWORD LogMsg = MAXDWORD; BOOL fWarn = FALSE; hr = pkcsVerifySignatureCertContext(NULL, NULL, 0, pCAContext, &LogMsg, &fWarn); if (S_OK != hr) { CSASSERT(MAXDWORD != LogMsg); LogEventStringHResult( fWarn? EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE, LogMsg, g_wszCommonName, hr); } } } HRESULT pkcsVerifyDSCACert( IN LDAP *pld) { HRESULT hr; HCAINFO hCAInfo = NULL; PCCERT_CONTEXT pDSCertContext = NULL; // BUGBUG: verify all of this CA's unexpired signature certs are in the DS. // Republish any that aren't. Cleans up DS replication conflicts. CSASSERT(g_pCAContextCurrent && g_pCAContextCurrent->pccCA); hr = CAFindByName( g_wszSanitizedName, NULL, CA_FIND_LOCAL_SYSTEM | CA_FIND_INCLUDE_UNTRUSTED, // this will cause findbyname to skip &hCAInfo); // ca cert checking _JumpIfErrorStr(hr, error, "CAFindByName", g_wszSanitizedName); hr = CAGetCACertificate(hCAInfo, &pDSCertContext); _JumpIfError(hr, error, "CAGetCACertificate"); if(!pDSCertContext || pDSCertContext->cbCertEncoded != g_pCAContextCurrent->pccCA->cbCertEncoded || 0 != memcmp(pDSCertContext->pbCertEncoded, g_pCAContextCurrent->pccCA->pbCertEncoded, g_pCAContextCurrent->pccCA->cbCertEncoded)) { // published cert is invalid or old, publish the current one hr = CASetCACertificate( hCAInfo, g_pCAContextCurrent->pccCA); _JumpIfError(hr, error, "CASetCACertificate"); hr = CAUpdateCA(hCAInfo); _JumpIfError(hr, error, "CAUpdateCA"); { CAuditEvent audit(SE_AUDITID_CERTSRV_PUBLISHCACERT, g_dwAuditFilter); hr = audit.AddData( // %1 Certificate Hash g_pCAContextCurrent->pccCA->pCertInfo->SerialNumber.pbData, g_pCAContextCurrent->pccCA->pCertInfo->SerialNumber.cbData); _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(g_pCAContextCurrent->pccCA->pCertInfo->NotBefore); // %2 Valid From _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(g_pCAContextCurrent->pccCA->pCertInfo->NotAfter); //%3 Valid To _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.Report(); _JumpIfError(hr, error, "CAuditEvent::Report"); } } hr = S_OK; error: if(hCAInfo) { CACloseCA(hCAInfo); } if(pDSCertContext) { CertFreeCertificateContext(pDSCertContext); } return(hr); } VOID pkcsReleaseKRACertArray() { DWORD i; if (NULL != g_rgKRACerts) { for (i = 0; i < g_cKRACerts; i++) { if (NULL != g_rgKRACerts[i]) { CertFreeCertificateContext(g_rgKRACerts[i]); } } LocalFree(g_rgKRACerts); g_rgKRACerts = NULL; } if (NULL != g_rgstrKRAHashes) { for (i = 0; i < g_cKRACerts; i++) { if (NULL != g_rgstrKRAHashes[i]) { SysFreeString(g_rgstrKRAHashes[i]); } } LocalFree(g_rgstrKRAHashes); g_rgstrKRAHashes = NULL; } g_cKRACerts = 0; g_hrKRALoad = S_OK; } VOID pkcsLogKRACertError( IN DWORD LogMsg, IN DWORD iHash, OPTIONAL IN CERT_CONTEXT const *pcc, IN HRESULT hrLog) { HRESULT hr; WCHAR awc[11]; WCHAR *pwszName = NULL; WCHAR const *pwszError = NULL; WCHAR const *apwsz[3]; wsprintf(awc, L"%u", iHash); apwsz[0] = awc; if (NULL != pcc) { hr = myCertNameToStr( X509_ASN_ENCODING, &pcc->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &pwszName); _PrintIfError(hr, "myCertNameToStr"); } apwsz[1] = NULL != pwszName? pwszName : L""; pwszError = myGetErrorMessageText(hrLog, TRUE); apwsz[2] = NULL != pwszError? pwszError : L""; LogEvent(EVENTLOG_ERROR_TYPE, LogMsg, ARRAYSIZE(apwsz), apwsz); //error: if (NULL != pwszName) { LocalFree(pwszName); } if (NULL != pwszError) { LocalFree(const_cast(pwszError)); } } HRESULT pkcsLoadKRACertContext( IN DWORD iHash, IN HCERTSTORE hStore) { HRESULT hr; CERT_CONTEXT const *pcc = NULL; BSTR strHash = NULL; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; DWORD cbHash; DWORD LogMsg = 0; BOOL fReloaded; DBGPRINT((DBG_SS_CERTSRV, "Loading KRA Cert[%u]:\n", iHash)); fReloaded = FALSE; while (TRUE) { hr = myFindCACertByHashIndex( hStore, g_wszSanitizedName, CSRH_CAKRACERT, iHash, NULL, // pNameId &pcc); if (S_OK == hr) { break; } if (fReloaded || CRYPT_E_NOT_FOUND != hr) { _JumpError(hr, error, "myFindCACertByHashIndex"); } _PrintError(hr, "myFindCACertByHashIndex"); // The KRA cert is missing from the HKLM "kra" store -- look it up in // the DB, and put it back in the store. hr = pkcsReloadMissingCAOrKRACert( hStore, g_wszSanitizedName, CSRH_CAKRACERT, iHash); _JumpIfError(hr, error, "pkcsReloadMissingCAOrKRACert"); fReloaded = TRUE; } hr = myVerifyKRACertContext( pcc, (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags)? CA_VERIFY_FLAGS_IGNORE_OFFLINE : 0); if (S_OK != hr) { LogMsg = MSG_E_INVALID_KRA_CERT; _JumpErrorStr(hr, error, "myVerifyKRACertContext", L"KRA cert invalid"); } hr = pkcsImportCAOrKRACert(pcc, DB_DISP_KRA_CERT, NULL); _JumpIfError(hr, error, "pkcsReloadMissingCAOrKRACert"); cbHash = sizeof(abHash); if (!CertGetCertificateContextProperty( pcc, CERT_SHA1_HASH_PROP_ID, abHash, &cbHash)) { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateContextProperty"); } hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); g_rgstrKRAHashes[g_cKRACerts] = strHash; g_rgKRACerts[g_cKRACerts] = pcc; g_cKRACerts++; strHash = NULL; pcc = NULL; hr = S_OK; error: if (S_OK != hr) { if (0 == LogMsg) { LogMsg = MSG_E_CANNOT_LOAD_KRA_CERT; } pkcsLogKRACertError(LogMsg, iHash, pcc, hr); } if (NULL != strHash) { SysFreeString(strHash); } if (NULL != pcc) { CertFreeCertificateContext(pcc); } return(hr); } HRESULT pkcsLoadKRACertArray() { HRESULT hr; DWORD iHash; DWORD i; HCERTSTORE hStore = NULL; DWORD LogMsg = 0; if (!g_fAdvancedServer) { LogMsg = MSG_E_KRA_NOT_ADVANCED_SERVER; hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); _JumpError(hr, error, "!g_fAdvancedServer"); } // open KRA store hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE, wszKRA_CERTSTORE); if (NULL == hStore) { hr = myHLastError(); _JumpError(hr, error, "CertOpenStore"); } // find & load KRA certs hr = myGetCARegHashCount(g_wszSanitizedName, CSRH_CAKRACERT, &g_cKRAHashes); if (S_OK == hr && 0 == g_cKRAHashes) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } _JumpIfError(hr, error, "myGetCARegHashCount"); g_rgKRACerts = (CERT_CONTEXT const **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, g_cKRAHashes * sizeof(g_rgKRACerts[0])); if (NULL == g_rgKRACerts) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } g_rgstrKRAHashes = (BSTR *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, g_cKRAHashes * sizeof(g_rgstrKRAHashes[0])); if (NULL == g_rgstrKRAHashes) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } for (iHash = 0; iHash < g_cKRAHashes; iHash++) { hr = pkcsLoadKRACertContext(iHash, hStore); if (S_OK != hr) { _PrintError(hr, "pkcsLoadKRACertContext"); g_hrKRALoad = hr; } } if (0 == g_cKRACerts) { LogMsg = MSG_E_NO_VALID_KRA_CERTS; hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "g_cKRACerts == 0"); } g_iKRACerts = 0; if (g_cKRACerts > g_cKRACertsRoundRobin) { srand((unsigned) time(NULL)); g_iKRACerts = rand() % g_cKRACerts; } else { g_cKRACertsRoundRobin = g_cKRACerts; } hr = S_OK; error: if (S_OK != hr) { if (0 == LogMsg) { LogMsg = MSG_E_LOADING_KRA_CERTS; } LogEventHResult(EVENTLOG_ERROR_TYPE, LogMsg, hr); pkcsReleaseKRACertArray(); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } return(hr); } HRESULT pkcsExpandURL( IN WCHAR const *pwszURLTemplate, OUT WCHAR **ppwszURL) { HRESULT hr; *ppwszURL = NULL; CSASSERT(NULL != g_strDomainDN && NULL != g_strConfigDN); hr = myFormatCertsrvStringArray( FALSE, // fURL g_pwszServerName, // pwszServerName_p1_2 g_wszSanitizedName, // pwszSanitizedName_p3_7 0, // iCert_p4 g_strDomainDN, // pwszDomainDN_p5 g_strConfigDN, // pwszConfigDN_p6 0, // iCRL_p8 FALSE, // fDeltaCRL_p9 FALSE, // fDSAttrib_p10_11 1, // cStrings &pwszURLTemplate, // apwszStringsIn ppwszURL); // apwszStringsOut _JumpIfError(hr, error, "myFormatCertsrvStringArray"); error: return(hr); } HRESULT pkcsPatchDN( IN HRESULT hrFail, IN WCHAR const *pwszRegName, IN OUT BSTR *pstrDSValue) { HRESULT hr; WCHAR *pwszRegValue = NULL; hr = myGetCertRegStrValue( g_wszSanitizedName, NULL, NULL, pwszRegName, &pwszRegValue); _PrintIfErrorStr(hr, "myGetCertRegStrValue", pwszRegName); if (NULL != *pstrDSValue) { if (NULL == pwszRegValue || 0 != lstrcmp(*pstrDSValue, pwszRegValue)) { // set reg value hr = mySetCertRegStrValue( g_wszSanitizedName, NULL, NULL, pwszRegName, *pstrDSValue); _PrintIfErrorStr(hr, "mySetCertRegStrValue", pwszRegName); } } else { if (NULL == pwszRegValue || L'\0' == *pwszRegValue) { hr = hrFail; _JumpError(hr, error, "both DS and Reg NULL"); } if (!ConvertWszToBstr(pstrDSValue, pwszRegValue, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToBstr"); } } hr = S_OK; error: if (NULL != pwszRegValue) { LocalFree(pwszRegValue); } return(hr); } HRESULT pkcsGetAuthoritativeDomainDn( IN WCHAR const *pwszCommonName, OUT LDAP **ppld, OUT BSTR *pstrDomainDN, OUT BSTR *pstrConfigDN) { HRESULT hr; WCHAR *pwszConfigDN = NULL; WCHAR *pwszDomainDN = NULL; // Get domain and config containers (%5, %6) *ppld = NULL; *pstrDomainDN = NULL; *pstrConfigDN = NULL; if (g_fUseDS) { HRESULT hr2; hr = myRobustLdapBind(ppld, FALSE); if (S_OK != hr) { _PrintError(hr, "myRobustLdapBind"); } else { hr = myGetAuthoritativeDomainDn(*ppld, pstrDomainDN, pstrConfigDN); _PrintIfError(hr, "myGetAuthoritativeDomainDn"); } if (S_OK != hr) { LogEventStringHResult( EVENTLOG_ERROR_TYPE, MSG_E_DS_RETRY, pwszCommonName, hr); } hr2 = hr; hr = pkcsPatchDN(hr2, wszREGDSCONFIGDN, pstrConfigDN); _JumpIfError(hr, error, "pkcsPatchDN"); hr = pkcsPatchDN(hr2, wszREGDSDOMAINDN, pstrDomainDN); _JumpIfError(hr, error, "pkcsPatchDN"); } else { *pstrDomainDN = SysAllocString(L""); *pstrConfigDN = SysAllocString(L""); if (NULL == *pstrDomainDN || NULL == *pstrConfigDN) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "SysAllocString"); } } hr = S_OK; error: return(hr); } HRESULT PKCSSetup( IN WCHAR const *pwszCommonName, IN WCHAR const *pwszSanitizedName) { HRESULT hr; LDAP *pld = NULL; DWORD LogMsg = MAXDWORD; BOOL fWarn = FALSE; // set crypt handles and load certificate chain hr = pkcsGetAuthoritativeDomainDn( pwszCommonName, &pld, &g_strDomainDN, &g_strConfigDN); if (S_OK != hr) { LogMsg = MSG_E_NO_DS; _JumpError(hr, error, "pkcsGetAuthoritativeDomainDn"); } // get (multiple) CRL path templates hr = pkcsLoadTemplates( wszREGCRLPUBLICATIONURLS, &g_paRevURL, &g_caRevURL); _PrintIfErrorStr(hr, "pkcsLoadTemplates", wszREGCRLPUBLICATIONURLS); // get (multiple) CA Cert path templates hr = pkcsLoadTemplates( wszREGCACERTPUBLICATIONURLS, &g_paCACertURL, &g_caCACertURL); _PrintIfErrorStr(hr, "pkcsLoadTemplates", wszREGCACERTPUBLICATIONURLS); hr = DBOpen(pwszSanitizedName); if (S_OK != hr) { LogMsg = MSG_E_DB_INIT_FAILED; _JumpError(hr, error, "PKCSSetup:DBOpen"); } hr = pkcsLoadCAContextArray(pwszCommonName, pwszSanitizedName); if (S_OK != hr) { LogMsg = MSG_E_CA_CHAIN; _JumpError(hr, error, "pkcsLoadCAContextArray"); } hr = pkcsImportCAContextArray(); _PrintIfError(hr, "pkcsImportCAContextArray"); hr = pkcsVerifySignatureCertContextArray(pwszCommonName, &LogMsg, &fWarn); _JumpIfError(hr, error, "pkcsVerifySignatureCertContextArray"); if (0 != g_cKRACertsRoundRobin) { hr = pkcsLoadKRACertArray(); _PrintIfError(hr, "pkcsLoadKRACertArray"); } hr = pkcsExpandURL(g_wszzLDAPKRACertURLTemplate, &g_pwszKRAPublishURL); _JumpIfError(hr, error, "pkcsExpandURL"); hr = pkcsExpandURL( g_wszzLDAPIssuerCertURLTemplate, &g_pwszAIACrossCertPublishURL); _JumpIfError(hr, error, "pkcsExpandURL"); hr = pkcsExpandURL( g_wszLDAPRootTrustURLTemplate, &g_pwszRootTrustCrossCertPublishURL); _JumpIfError(hr, error, "pkcsExpandURL"); if (NULL != pld) { hr = pkcsVerifyDSCACert(pld); _PrintIfError(hr, "pkcsVerifyDSCACert"); } hr = S_OK; error: if (NULL != pld) { ldap_unbind(pld); } if (S_OK != hr) { CSASSERT(MAXDWORD != LogMsg); if (!fWarn) { PKCSTerminate(); } LogEventStringHResult( fWarn? EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE, LogMsg, pwszCommonName, hr); if (fWarn) { hr = S_OK; } } return(hr); } VOID pkcsReleaseCACertificateChain( CERT_CONTEXT const **apCACertChain, DWORD cCACertChain) { DWORD i; if (NULL != apCACertChain) { for (i = 0; i < cCACertChain; ++i) { CertFreeCertificateContext(apCACertChain[i]); } LocalFree(apCACertChain); } } VOID pkcsReleaseCAContext( IN OUT CACTX *pCAContext) { pkcsReleaseCACertificateChain( pCAContext->apCACertChain, pCAContext->cCACertChain); //pCAContext->apCACertChain = NULL; //pCAContext->pccCA = NULL; if (NULL != pCAContext->hProvCA) { CryptReleaseContext(pCAContext->hProvCA, 0); } if (NULL != pCAContext->IssuerKeyId.pbData) { LocalFree(pCAContext->IssuerKeyId.pbData); } if (NULL != pCAContext->pszObjIdSignatureAlgorithm) { LocalFree(pCAContext->pszObjIdSignatureAlgorithm); } if (NULL != pCAContext->KeyAuthority2Cert.pbData) { LocalFree(pCAContext->KeyAuthority2Cert.pbData); } if (NULL != pCAContext->KeyAuthority2CRL.pbData) { LocalFree(pCAContext->KeyAuthority2CRL.pbData); } if (NULL != pCAContext->CDPCert.pbData) { LocalFree(pCAContext->CDPCert.pbData); } if (NULL != pCAContext->CDPCRLFreshest.pbData) { LocalFree(pCAContext->CDPCRLFreshest.pbData); } if (NULL != pCAContext->CDPCRLBase.pbData) { LocalFree(pCAContext->CDPCRLBase.pbData); } if (NULL != pCAContext->CDPCRLDelta.pbData) { LocalFree(pCAContext->CDPCRLDelta.pbData); } if (NULL != pCAContext->AIACert.pbData) { LocalFree(pCAContext->AIACert.pbData); } if (NULL != pCAContext->pwszKeyContainerName) { LocalFree(pCAContext->pwszKeyContainerName); } if (NULL != pCAContext->papwszCRLFiles) { WCHAR **ppwsz; for (ppwsz = pCAContext->papwszCRLFiles; NULL != *ppwsz; ppwsz++) { LocalFree(*ppwsz); } LocalFree(pCAContext->papwszCRLFiles); } if (NULL != pCAContext->papwszDeltaCRLFiles) { WCHAR **ppwsz; for (ppwsz = pCAContext->papwszDeltaCRLFiles; NULL != *ppwsz; ppwsz++) { LocalFree(*ppwsz); } LocalFree(pCAContext->papwszDeltaCRLFiles); } } VOID pkcsReleaseCAContextArray() { DWORD i; if (NULL != g_aCAContext) { for (i = 0; i < g_cCACerts; i++) { pkcsReleaseCAContext(&g_aCAContext[i]); } LocalFree(g_aCAContext); g_aCAContext = NULL; } g_cCACerts = 0; g_pCAContextCurrent = NULL; } // Trim off leading and trailing whitespace and separator characters WCHAR * pkcsTrimToken( IN WCHAR *pwszIn, IN WCHAR wchSeparator) { WCHAR *pwsz; while (wchSeparator == *pwszIn || iswspace(*pwszIn)) { pwszIn++; } pwsz = &pwszIn[wcslen(pwszIn)]; while (--pwsz >= pwszIn && (wchSeparator == *pwsz || iswspace(*pwsz))) { *pwsz = L'\0'; } if (L'\0' == *pwszIn) { pwszIn = NULL; } return(pwszIn); } WCHAR * PKCSSplitToken( IN OUT WCHAR **ppwszIn, IN WCHAR *pwcSeparator, OUT BOOL *pfSplit) { WCHAR *pwszOut = NULL; WCHAR *pwszNext = NULL; BOOL fSplit = FALSE; WCHAR *pwszIn; WCHAR *pwsz; pwszIn = *ppwszIn; if (NULL != pwszIn) { pwszOut = pwszIn; if (NULL != pwcSeparator) { pwsz = wcschr(pwszIn, *pwcSeparator); if (NULL != pwsz) { *pwsz = L'\0'; pwszNext = pkcsTrimToken(&pwsz[1], *pwcSeparator); pwszOut = pkcsTrimToken(pwszOut, *pwcSeparator); fSplit = TRUE; } } } *ppwszIn = pwszNext; *pfSplit = fSplit; return(pwszOut); } HRESULT PKCSSetSubjectTemplate( IN WCHAR const *pwszzTemplate) { HRESULT hr; BOOL fSplit; WCHAR const *pwszz; WCHAR const *pwszPropName; BSTR bstrToken; SUBJECTTABLE const **ppSubject; SUBJECTTABLE const **pps; SUBJECTTABLE const *pSubject; DWORD dwIndex; DWORD cchMax; hr = E_INVALIDARG; if (NULL == pwszzTemplate) { _JumpError(hr, error, "pwszzTemplate NULL"); } ppSubject = pkcs_apSubject; // fill in this empty subject array with string matches for (pwszz = pwszzTemplate; L'\0' != *pwszz; pwszz += wcslen(pwszz) + 1) { pwszPropName = PKCSMapAttributeName(pwszz, NULL, &dwIndex, &cchMax); if (NULL == pwszPropName) { hr = E_INVALIDARG; _JumpErrorStr(hr, error, "PKCSMapAttributeName", pwszz); } for (pSubject = pkcs_subject; ; pSubject++) { if (NULL == pSubject->pwszPropName) { _JumpError(hr, error, "pkcs_subject lookup"); } if (0 == lstrcmpi(pSubject->pwszPropName, pwszPropName)) { break; } } for (pps = pkcs_apSubject; pps < ppSubject; pps++) { if (*pps == pSubject) { _JumpErrorStr(hr, error, "pkcs_subject duplicate", pwszz); } } if (ppSubject >= &pkcs_apSubject[CSUBJECTTABLE]) { _JumpError(hr, error, "pkcs_subject overflow"); } DBGPRINT(( DBG_SS_CERTSRVI, "Subject Template[%u]: %hs -- %ws\n", SAFE_SUBTRACT_POINTERS(ppSubject, pkcs_apSubject), pSubject->pszObjId, pSubject->pwszPropName)); *ppSubject++ = pSubject; } CSASSERT(ppSubject <= &pkcs_apSubject[CSUBJECTTABLE]); if (ppSubject == pkcs_apSubject) { _JumpError(hr, error, "pwszzTemplate empty"); } pkcs_ppSubjectLast = ppSubject - 1; pkcsfSubjectTemplate = TRUE; hr = S_OK; error: return(hr); } HRESULT pkcsSplitRDNComponents( IN SUBJECTTABLE const *pSubjectTable, IN OUT WCHAR *pwszRDN, // Parsing stomps string in-place IN DWORD cAttrMax, OUT DWORD *pcAttr, OUT CERT_RDN_ATTR *rgAttr) { HRESULT hr; DWORD cAttr; DWORD i; DWORD cwc; WCHAR *pwszRemain; WCHAR const *pwszToken; WCHAR *pwszT; BOOL fSplit; *pcAttr = 0; cAttr = 0; if (NULL != pwszRDN) { // Allocate memory for each RDN component filled in: pwszRemain = pwszRDN; while (TRUE) { pwszToken = PKCSSplitToken( &pwszRemain, wszNAMESEPARATORDEFAULT, &fSplit); if (NULL == pwszToken) { break; } if (cAttr >= cAttrMax) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpError(hr, error, "Subject RDN overflow"); } cwc = wcslen(pwszToken); if (g_fEnforceRDNNameLengths && cwc > pSubjectTable->cchMax) { DBGPRINT(( DBG_SS_CERTSRV, "RDN component too long: %u/%u: %ws[%u]=\"%ws\"\n", cwc, pSubjectTable->cchMax, pSubjectTable->pwszPropName, cAttr, pwszToken)); hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpErrorStr(hr, error, "RDN component too long", pwszToken); } pwszT = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == pwszT) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc(pwszToken)"); } wcscpy(pwszT, pwszToken); rgAttr[cAttr].pszObjId = (char *) pSubjectTable->pszObjId; rgAttr[cAttr].dwValueType = CERT_RDN_ANY_TYPE; // 'best' encoding rgAttr[cAttr].Value.pbData = (BYTE *) pwszT; rgAttr[cAttr].Value.cbData = 0; // Indicate Unicode input cAttr++; } } *pcAttr = cAttr; hr = S_OK; error: if (S_OK != hr) { for (i = 0; i < cAttr; i++) { LocalFree(rgAttr[i].Value.pbData); } } return(hr); } #define CSUBJECTRDNMAX (4 * CSUBJECTTABLE) HRESULT pkcsEncodeSubjectName( IN ICertDBRow *prow, IN CERT_RDN_ATTR const *rgAttr, IN DWORD cAttr, OUT BYTE **ppbData, OUT DWORD *pcbData) { HRESULT hr; DWORD i; DWORD cbprop; DWORD dwRequestFlags; DWORD dwFlags; CERT_RDN rgRDN[CSUBJECTRDNMAX]; CERT_NAME_INFO nameinfo; CSASSERT(ARRAYSIZE(rgRDN) >= cAttr); for (i = 0; i < cAttr; i++) { rgRDN[i].cRDNAttr = 1; rgRDN[i].rgRDNAttr = (CERT_RDN_ATTR *) &rgAttr[i]; } nameinfo.cRDN = cAttr; nameinfo.rgRDN = rgRDN; cbprop = sizeof(dwRequestFlags); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbprop, (BYTE *) &dwRequestFlags); _JumpIfError(hr, error, "GetProperty"); CSASSERT(sizeof(dwRequestFlags) == cbprop); dwFlags = 0; if (CR_FLG_FORCETELETEX & dwRequestFlags) { dwFlags |= CERT_RDN_ENABLE_T61_UNICODE_FLAG; } if (CR_FLG_FORCEUTF8 & dwRequestFlags) { dwFlags |= CERT_RDN_ENABLE_UTF8_UNICODE_FLAG; } if (!myEncodeName( X509_ASN_ENCODING, &nameinfo, dwFlags, CERTLIB_USE_LOCALALLOC, ppbData, pcbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeName"); } error: return(hr); } HRESULT pkcsBuildSubjectFromNamesTable( IN ICertDBRow *prow, OUT CERT_NAME_BLOB *pSubject) { HRESULT hr; DWORD cbData = 0; DWORD i; DWORD cAttr; DWORD cAttrT; CERT_RDN_ATTR rgAttr[CSUBJECTRDNMAX]; SUBJECTTABLE const * const *ppSubject; WCHAR *pwszData = NULL; pSubject->pbData = NULL; CSASSERT(NULL != pkcs_ppSubjectLast); cAttr = 0; for ( ppSubject = pkcs_ppSubjectLast; ppSubject >= pkcs_apSubject; ppSubject--) { hr = PKCSGetProperty( prow, (*ppSubject)->pwszPropName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbData, (BYTE **) &pwszData); if (S_OK != hr) { continue; } if (0 != cbData) { // Allocates memory for each RDN component filled in: hr = pkcsSplitRDNComponents( *ppSubject, pwszData, ARRAYSIZE(rgAttr) - cAttr, &cAttrT, &rgAttr[cAttr]); _JumpIfError(hr, error, "SplitRDNComponents"); cAttr += cAttrT; } LocalFree(pwszData); pwszData = NULL; } // done building string of subject entries, time to encode hr = pkcsEncodeSubjectName( prow, rgAttr, cAttr, &pSubject->pbData, &pSubject->cbData); _JumpIfError(hr, error, "pkcsEncodeSubjectName"); hr = prow->SetProperty( g_wszPropSubjectRawName, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, pSubject->cbData, pSubject->pbData); _JumpIfError(hr, error, "SetProperty"); pkcsSetDistinguishedName(prow, PROPTABLE_CERTIFICATE, pSubject); error: for (i = 0; i < cAttr; i++) { LocalFree(rgAttr[i].Value.pbData); } if (NULL != pwszData) { LocalFree(pwszData); } return(hr); } HRESULT pkcsCheck7f( IN ICertDBRow *prow, IN BYTE const *pbCert, IN DWORD cbCert, OUT BOOL *pfErrorLogged) { HRESULT hr; DWORD cbProp; WCHAR awcSubject[1024]; WCHAR wszDword[2+8+1]; WCHAR wszRequestId[11+1]; WCHAR const *pwszDword; WORD cString = 0; WCHAR const *apwsz[4]; DWORD State; DWORD Index1; DWORD Index2; DWORD cwcField; WCHAR wszField[128]; DWORD cwcObjectId; WCHAR wszObjectId[128]; WCHAR const *pwszObjectIdDescription = NULL; WCHAR *wszBuf=NULL; const DWORD dwDefaultBufSize = 2048*sizeof(WCHAR); *pfErrorLogged = FALSE; cwcField = sizeof(wszField)/sizeof(wszField[0]); cwcObjectId = sizeof(wszObjectId)/sizeof(wszObjectId[0]); hr = myCheck7f( pbCert, cbCert, FALSE, &State, &Index1, &Index2, &cwcField, wszField, &cwcObjectId, wszObjectId, &pwszObjectIdDescription); // Static: do not free! _JumpIfError(hr, error, "myCheck7f"); if (CHECK7F_NONE != State) { DWORD ReqId; wszBuf = (WCHAR*)LocalAlloc(LMEM_FIXED, dwDefaultBufSize); if (NULL == wszBuf) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } prow->GetRowId(&ReqId); wsprintf(wszRequestId, L"%u", ReqId); apwsz[cString++] = wszRequestId; apwsz[cString++] = wszDword; cbProp = sizeof(awcSubject); hr = prow->GetProperty( g_wszPropSubjectDistinguishedName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, (BYTE *) awcSubject); if (S_OK != hr) { _PrintError(hr, "GetProperty(DN)"); wcscpy(awcSubject, L"???"); } apwsz[cString++] = awcSubject; wcscpy(wszBuf, wszField); if (0 != Index1) { wsprintf( &wszBuf[wcslen(wszBuf)], 0 != Index2? L"[%u,%u]" : L"[%u]", Index1 - 1, Index2 - 1); } if (0 != cwcObjectId) { wcscat(wszBuf, L" ObjectId="); wcscat(wszBuf, wszObjectId); } if (NULL != pwszObjectIdDescription) { // If buffer too small, reallocate enough space for old buffer, // OID description, () and trailing zero DWORD dwBufLen = (wcslen(wszBuf)+wcslen(pwszObjectIdDescription)+3)* sizeof(WCHAR); if(dwDefaultBufSizeGetRowId(&dw); CopyMemory(pb, &dw, sizeof(dw)); pb += sizeof(dw); us = (USHORT) pCAContext->iCert; CopyMemory(pb, &us, sizeof(us)); pb += sizeof(us); if (0 != g_dwHighSerial) { if (!CryptGenRandom( g_pCAContextCurrent->hProvCA, ARRAYSIZE(abRandom), abRandom)) { hr = myHLastError(); _PrintError(hr, "CryptGenRandom"); memset(abRandom, g_dwHighSerial, sizeof(abRandom)); } CopyMemory(pb, abRandom, sizeof(abRandom)); pb += sizeof(abRandom); CopyMemory(pb, &dw, sizeof(dw)); pb += sizeof(dw); *pb++ = (BYTE) g_dwHighSerial; } else { dw = GetTickCount(); CopyMemory(pb, &dw, sizeof(dw)); pb += sizeof(dw); } cbSerial = SAFE_SUBTRACT_POINTERS(pb, abSerial); // Make sure the sreial number doesn't overflow the buffer: CSASSERT(sizeof(abSerial) >= cbSerial); // IETF max serial number length is 20 bytes: CSASSERT(20 >= cbSerial); pb--; if (0 == *pb) { *pb = 'a'; } else if (0 == (0xf0 & *pb)) { *pb |= 0x10; // make high nibble non-zero } *pb &= 0x7f; // Some clients can't handle negative serial numbers: #ifdef TEST_SPECIAL_SERIAL_NUMBERS if (1 & abSerial[0]) { *pb |= 0x80; // Test negative serial numbers: if (2 & abSerial[0]) { *pb-- = 0; // Test high zero byte serial numbers: *pb |= 0x80; // Test negative serial numbers: fAddZeroByte = TRUE; } } #endif hr = MultiByteIntegerToBstr(FALSE, cbSerial, abSerial, &strSerialNumber); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); #ifdef TEST_SPECIAL_SERIAL_NUMBERS if (fAddZeroByte) { BSTR str = NULL; str = SysAllocStringLen(NULL, 2 + wcslen(strSerialNumber)); if (NULL != str) { wcscpy(str, L"00"); wcscat(str, strSerialNumber); SysFreeString(strSerialNumber); strSerialNumber = str; } } #endif *pstrSerialNumber = strSerialNumber; strSerialNumber = NULL; hr = S_OK; error: if (NULL != strSerialNumber) { SysFreeString(strSerialNumber); } return(hr); } HRESULT PKCSVerifySubjectRDN( IN ICertDBRow *prow, IN WCHAR const *pwszPropertyName, OPTIONAL IN WCHAR const *pwszPropertyValue, OUT BOOL *pfSubjectDot) { HRESULT hr; WCHAR const *pwsz; WCHAR const *pwszName = pwszPropertyName; WCHAR wszPrefix[ARRAYSIZE(wszPROPSUBJECTDOT)]; SUBJECTTABLE const *pSubjectTable; DWORD i; DWORD cAttr = 0; CERT_RDN_ATTR rgAttr[CSUBJECTRDNMAX]; WCHAR *pwszValue = NULL; DWORD cbData; BYTE *pbData = NULL; hr = S_OK; *pfSubjectDot = FALSE; // Check to see if the request is for L"Subject.". pwsz = wcschr(pwszName, L'.'); if (NULL != pwsz && SAFE_SUBTRACT_POINTERS(pwsz, pwszName) + 2 == ARRAYSIZE(wszPrefix)) { pwsz++; // skip past L'.' CopyMemory( wszPrefix, pwszName, (SAFE_SUBTRACT_POINTERS(pwsz, pwszName) * sizeof(WCHAR))); wszPrefix[ARRAYSIZE(wszPrefix) - 1] = L'\0'; if (0 == lstrcmpi(wszPrefix, wszPROPSUBJECTDOT)) { pwszName = pwsz; if (L'\0' == *pwszName) { *pfSubjectDot = TRUE; } } } if (!*pfSubjectDot) { for (pSubjectTable = pkcs_subject; ; pSubjectTable++) { WCHAR const * const *ppwsz; if (NULL == pSubjectTable->pwszPropName) { goto error; } // Check for matching full name without "Subject." prefix: pwsz = wcschr(pSubjectTable->pwszPropName, L'.'); if (NULL != pwsz && 0 == lstrcmpi(pwszName, &pwsz[1])) { break; } // Check for matching OID: if (!iswdigit(*pwszName)) { continue; } for ( ppwsz = pSubjectTable->apwszAttributeName; NULL != *ppwsz; ppwsz++) { if (*pwszName == **ppwsz && 0 == lstrcmp(pwszName, *ppwsz)) { break; } } if (NULL != *ppwsz) { break; } } } // It's a valid Certificate Table Subject RDN. Call pkcsSplitRDNComponents // to split the string into individual RDN components and optionally // enforce each component is under the maximum length. DBGPRINT(( DBG_SS_CERTSRVI, "PKCSVerifySubjectRDN(%ws) --> '%ws'\n", pwszPropertyName, pwszName)); if (!*pfSubjectDot && NULL != pwszPropertyValue) { pwszValue = (WCHAR *) LocalAlloc( LMEM_FIXED, (wcslen(pwszPropertyValue) + 1) * sizeof(WCHAR)); if (NULL == pwszValue) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } wcscpy(pwszValue, pwszPropertyValue); hr = pkcsSplitRDNComponents( pSubjectTable, pwszValue, ARRAYSIZE(rgAttr), &cAttr, rgAttr); _JumpIfError(hr, error, "SplitRDNComponents"); // Call myEncodeName merely to test for valid string data. // Some RDN OIDs are restricted to IA5 strings. hr = pkcsEncodeSubjectName(prow, rgAttr, cAttr, &pbData, &cbData); _JumpIfError(hr, error, "pkcsEncodeSubjectName"); } hr = PKCSSetRequestFlags(prow, FALSE, CR_FLG_SUBJECTUNMODIFIED); _JumpIfError(hr, error, "PKCSSetRequestFlags"); error: for (i = 0; i < cAttr; i++) { LocalFree(rgAttr[i].Value.pbData); } if (NULL != pbData) { LocalFree(pbData); } if (NULL != pwszValue) { LocalFree(pwszValue); } return(hr); } HRESULT PKCSDeleteAllSubjectRDNs( IN ICertDBRow *prow, IN DWORD Flags) { HRESULT hr; SUBJECTTABLE const *pSubjectTable; WCHAR const *pwszPropName = NULL; for (pSubjectTable = pkcs_subject; ; pSubjectTable++) { if (NULL == pSubjectTable->pwszPropName) { break; } hr = prow->SetProperty(pSubjectTable->pwszPropName, Flags, 0, NULL); _JumpIfError(hr, error, "SetProperty"); } hr = S_OK; error: return(hr); } HRESULT pkcsverifyIssuedPolices( IN CERT_CONTEXT const *pcc, IN CHAR const *pszObjId, OPTIONAL IN WCHAR const *pwszzPolicies) { HRESULT hr; CERT_EXTENSION const *pExt; CERT_POLICIES_INFO *pcpsi = NULL; DWORD cb; DWORD i; WCHAR const *pwsz; WCHAR *pwszObjId = NULL; if (NULL == pwszzPolicies) { hr = S_OK; goto error; } pExt = CertFindExtension( pszObjId, pcc->pCertInfo->cExtension, pcc->pCertInfo->rgExtension); if (NULL == pExt) { hr = S_OK; goto error; } if (!myDecodeObject( X509_ASN_ENCODING, X509_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pcpsi, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } for (i = 0; i < pcpsi->cPolicyInfo; i++) { CSASSERT(NULL == pwszObjId); if (!myConvertSzToWsz( &pwszObjId, pcpsi->rgPolicyInfo[i].pszPolicyIdentifier, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "myConvertSzToWsz(ObjId)"); } for (pwsz = pwszzPolicies; ; pwsz += wcslen(pwsz) + 1) { if (L'\0' == *pwsz) { hr = CERT_E_INVALID_POLICY; _JumpErrorStr(hr, error, "Chain invalidates policy", pwszObjId); } if (0 == lstrcmp(pwsz, pwszObjId)) { break; } } LocalFree(pwszObjId); pwszObjId = NULL; } hr = S_OK; error: if (NULL != pcpsi) { LocalFree(pcpsi); } if (NULL != pwszObjId) { LocalFree(pwszObjId); } return(hr); } HRESULT pkcsEncodeSubjectCert( IN ICertDBRow *prow, IN CACTX const *pCAContext, OUT BYTE **ppbEncoded, // CoTaskMem* OUT DWORD *pcbEncoded, OUT BOOL *pfErrorLogged) { HRESULT hr; HRESULT hrValidate = S_OK; BYTE *pbCertEncoded = NULL; DWORD cbCertEncoded; DWORD ExtFlags; BSTR strSerialNumber = NULL; IEnumCERTDBNAME *penum = NULL; CERTDBNAME cdbn; FILETIME ftNotBefore; DWORD dwRequestFlags; WCHAR *pwszIssuer = NULL; WCHAR *pwszSubject = NULL; CERT_INFO Cert; CERT_EXTENSION *pExt = NULL; DWORD cExt = INCREMENT_EXTENSIONS; DWORD cbprop; DWORD i; CHAR *pChar; CHAR szObjId[MAX_PATH]; BYTE *pb; CERT_CONTEXT const *pcc = NULL; WCHAR *pwszzIssuancePolicies = NULL; WCHAR *pwszzApplicationPolicies = NULL; cdbn.pwszName = NULL; *pfErrorLogged = FALSE; // CERT ZeroMemory(&Cert, sizeof(Cert)); pExt = (CERT_EXTENSION *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cExt * sizeof(*pExt)); if (NULL == pExt) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } Cert.dwVersion = CERT_V3; hr = pkcsCreateCertSerialNumber(prow, pCAContext, &strSerialNumber); _JumpIfError(hr, error, "pkcsCreateCertSerialNumber"); // convert to int hr = WszToMultiByteInteger( FALSE, strSerialNumber, &Cert.SerialNumber.cbData, &Cert.SerialNumber.pbData); _JumpIfError(hr, error, "WszToMultiByteInteger"); Cert.SignatureAlgorithm.pszObjId = pCAContext->pszObjIdSignatureAlgorithm; Cert.Issuer = pCAContext->pccCA->pCertInfo->Subject; cbprop = sizeof(Cert.NotBefore); hr = prow->GetProperty( g_wszPropCertificateNotBeforeDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbprop, (BYTE *) &Cert.NotBefore); _JumpIfError(hr, error, "GetProperty"); CSASSERT(sizeof(Cert.NotBefore) == cbprop); cbprop = sizeof(Cert.NotAfter); hr = prow->GetProperty( g_wszPropCertificateNotAfterDate, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbprop, (BYTE *) &Cert.NotAfter); _JumpIfError(hr, error, "GetProperty"); CSASSERT(sizeof(Cert.NotAfter) == cbprop); CSASSERT(NULL == Cert.Subject.pbData); cbprop = sizeof(dwRequestFlags); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbprop, (BYTE *) &dwRequestFlags); _JumpIfError(hr, error, "GetProperty"); if (!pkcsfSubjectTemplate || ((CRLF_REBUILD_MODIFIED_SUBJECT_ONLY & g_dwCRLFlags) && (CR_FLG_SUBJECTUNMODIFIED & dwRequestFlags))) { hr = PKCSGetProperty( prow, g_wszPropSubjectRawName, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, &Cert.Subject.cbData, &Cert.Subject.pbData); if (S_OK == hr && 0 == Cert.Subject.cbData && NULL != Cert.Subject.pbData) { LocalFree(Cert.Subject.pbData); Cert.Subject.pbData = NULL; } } if (NULL == Cert.Subject.pbData) { hr = pkcsBuildSubjectFromNamesTable(prow, &Cert.Subject); _JumpIfError(hr, error, "pkcsBuildSubjectFromNamesTable"); } if (CertCompareCertificateName( X509_ASN_ENCODING, &Cert.Issuer, &Cert.Subject)) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpError(hr, error, "Subject matches Issuer"); } hr = myCertNameToStr( X509_ASN_ENCODING, &Cert.Issuer, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &pwszIssuer); _JumpIfError(hr, error, "myCertNameToStr"); hr = myCertNameToStr( X509_ASN_ENCODING, &Cert.Subject, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &pwszSubject); _JumpIfError(hr, error, "myCertNameToStr"); if (0 == lstrcmpi(pwszIssuer, pwszSubject)) { hr = CERTSRV_E_BAD_REQUESTSUBJECT; _JumpError(hr, error, "Subject string matches Issuer"); } hr = pkcsGetPublicKeyInfo(prow, &Cert.SubjectPublicKeyInfo); _JumpIfError(hr, error, "pkcsGetPublicKeyInfo"); Cert.rgExtension = pExt; i = 0; hr = prow->EnumCertDBName(CIE_TABLE_EXTENSIONS, &penum); _JumpIfError(hr, error, "EnumCertDBName"); hr = CERTSRV_E_PROPERTY_EMPTY; while (TRUE) { ULONG celtFetched; if (cExt == i) { CERT_EXTENSION *pExtT; // reached max, increse size cExt += INCREMENT_EXTENSIONS; pExtT = (CERT_EXTENSION *) LocalReAlloc( pExt, cExt * sizeof(*pExt), LMEM_ZEROINIT | LMEM_MOVEABLE); if (NULL == pExtT) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalReAlloc"); } pExt = pExtT; Cert.rgExtension = pExt; } hr = penum->Next(1, &cdbn, &celtFetched); if (S_FALSE == hr) { break; } _JumpIfError(hr, error, "Next"); CSASSERT(1 == celtFetched); CSASSERT(NULL != cdbn.pwszName); if (!ConvertWszToSz( &Cert.rgExtension[i].pszObjId, cdbn.pwszName, -1)) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "ConvertWszToSz(ExtObjId)"); } hr = PropGetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, cdbn.pwszName, &ExtFlags, &Cert.rgExtension[i].Value.cbData, &Cert.rgExtension[i].Value.pbData); _JumpIfError(hr, error, "PropGetExtension"); DBGPRINT(( DBG_SS_CERTSRVI, "pkcsEncodeSubjectCert: Ext=%ws, ExtFlags=%x, len=%x\n", cdbn.pwszName, ExtFlags, Cert.rgExtension[i].Value.cbData)); Cert.rgExtension[i].fCritical = (EXTENSION_CRITICAL_FLAG & ExtFlags)? TRUE : FALSE; CoTaskMemFree(cdbn.pwszName); cdbn.pwszName = NULL; if (EXTENSION_DISABLE_FLAG & ExtFlags) { if (NULL != pExt[i].pszObjId) { LocalFree(pExt[i].pszObjId); pExt[i].pszObjId = NULL; } if (NULL != pExt[i].Value.pbData) { LocalFree(pExt[i].Value.pbData); pExt[i].Value.pbData = NULL; } continue; } i++; } Cert.cExtension = i; // encode the cert contents if (!myEncodeObject( X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, &Cert, 0, CERTLIB_USE_LOCALALLOC, &pbCertEncoded, &cbCertEncoded)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } // sign the cert, then encode the signed info hr = myEncodeSignedContent( pCAContext->hProvCA, X509_ASN_ENCODING, Cert.SignatureAlgorithm.pszObjId, pbCertEncoded, cbCertEncoded, CERTLIB_USE_COTASKMEMALLOC, ppbEncoded, pcbEncoded); // use CoTaskMem* _JumpIfError(hr, error, "myEncodeSignedContent"); pcc = CertCreateCertificateContext( X509_ASN_ENCODING, *ppbEncoded, *pcbEncoded); if (NULL == pcc) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } if (g_fCertEnrollCompatible) { hr = pkcsCheck7f( prow, *ppbEncoded, *pcbEncoded, pfErrorLogged); if (S_OK != hr) { CoTaskMemFree(*ppbEncoded); *ppbEncoded = NULL; _JumpError(hr, error, "pkcsCheck7f"); } } ftNotBefore = pcc->pCertInfo->NotBefore; myMakeExprDateTime(&ftNotBefore, g_dwClockSkewMinutes, ENUM_PERIOD_MINUTES); hr = myVerifyCertContextEx( pcc, // pCert (CRLF_REVCHECK_IGNORE_OFFLINE & g_dwCRLFlags)? CA_VERIFY_FLAGS_IGNORE_OFFLINE : 0, // dwFlags 0, // cUsageOids NULL, // apszUsageOids HCCE_LOCAL_MACHINE, // hChainEngine &ftNotBefore, // pft NULL, // hAdditionalStore NULL, // ppwszMissingIssuer &pwszzIssuancePolicies, &pwszzApplicationPolicies); _PrintIfError(hr, "myVerifyCertContextEx"); if (S_OK != hr) { if (0 == (CRLF_SAVE_FAILED_CERTS & g_dwCRLFlags)) { goto error; } hrValidate = hr; } if (S_OK == hrValidate && 0 == (CRLF_IGNORE_INVALID_POLICIES & g_dwCRLFlags)) { hr = pkcsverifyIssuedPolices( pcc, szOID_CERT_POLICIES, pwszzIssuancePolicies); _PrintIfError(hr, "pkcsverifyIssuedPolices"); if (S_OK != hr) { if (0 == (CRLF_SAVE_FAILED_CERTS & g_dwCRLFlags)) { goto error; } if (S_OK == hrValidate) { hrValidate = hr; } } hr = pkcsverifyIssuedPolices( pcc, szOID_APPLICATION_CERT_POLICIES, pwszzApplicationPolicies); _PrintIfError(hr, "pkcsverifyIssuedPolices"); if (S_OK != hr) { if (0 == (CRLF_SAVE_FAILED_CERTS & g_dwCRLFlags)) { goto error; } if (S_OK == hrValidate) { hrValidate = hr; } } } hr = pkcsSetCertAndKeyHashes(prow, pcc); _JumpIfError(hr, error, "pkcsSetCertAndKeyHashes"); hr = prow->SetProperty( g_wszPropCertificateIssuerNameID, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, sizeof(pCAContext->NameId), (BYTE *) &pCAContext->NameId); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( g_wszPropCertificateSerialNumber, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE *) strSerialNumber); _JumpIfError(hr, error, "SetProperty"); #ifdef TEST_SPECIAL_SERIAL_NUMBERS if (L'0' == strSerialNumber[0] && L'0' == strSerialNumber[1]) { hr = prow->SetProperty( g_wszPropCertificateSerialNumber, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE *) &strSerialNumber[2]); _JumpIfError(hr, error, "SetProperty"); } #endif error: if (S_OK != hrValidate) { hr = hrValidate; } if (NULL != pwszIssuer) { LocalFree(pwszIssuer); } if (NULL != pwszSubject) { LocalFree(pwszSubject); } if (NULL != pwszzIssuancePolicies) { LocalFree(pwszzIssuancePolicies); } if (NULL != pwszzApplicationPolicies) { LocalFree(pwszzApplicationPolicies); } if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (NULL != pExt) { i = 0; if (NULL != cdbn.pwszName) { CoTaskMemFree(cdbn.pwszName); } while (cExt != i) { if (NULL != pExt[i].pszObjId) { LocalFree(pExt[i].pszObjId); } if (NULL != pExt[i].Value.pbData) { LocalFree(pExt[i].Value.pbData); } i++; } LocalFree(pExt); } if (NULL != penum) { penum->Release(); } if (NULL != Cert.SerialNumber.pbData) { LocalFree(Cert.SerialNumber.pbData); } if (NULL != Cert.Subject.pbData) { LocalFree(Cert.Subject.pbData); } pkcsFreePublicKeyInfo(&Cert.SubjectPublicKeyInfo); if (NULL != pbCertEncoded) { LocalFree(pbCertEncoded); } if (NULL != strSerialNumber) { SysFreeString(strSerialNumber); } return(hr); } VOID pkcsFreeCRLChain( IN DWORD cCert, OPTIONAL IN OUT CERT_BLOB *prgCertBlob, OPTIONAL IN OUT CERT_CONTEXT const **rgCert, IN DWORD cCRL, OPTIONAL IN OUT CRL_BLOB *rgCRLBlob, OPTIONAL IN OUT CRL_CONTEXT const **rgCRL) { DWORD i; if (NULL != prgCertBlob) { LocalFree(prgCertBlob); } if (NULL != rgCert) { for (i = 0; i < cCert; i++) { if (NULL != rgCert[i]) { CertFreeCertificateContext(rgCert[i]); } } LocalFree(rgCert); } if (NULL != rgCRLBlob) { LocalFree(rgCRLBlob); } if (NULL != rgCRL) { for (i = 0; i < cCRL; i++) { if (NULL != rgCRL[i]) { CertFreeCRLContext(rgCRL[i]); } } LocalFree(rgCRL); } } // Build the CA's cert chain and collect all paremt CA CRLs. // Add in the optional passed leaf cert and the CA's CRLs. // This ensures that the chain includes at least this CA's correct cert & CRLs. HRESULT pkcsBuildCRLChain( OPTIONAL IN CACTX *pCAContext, OPTIONAL IN BYTE const *pbCertLeaf, IN DWORD cbCertLeaf, IN BOOL fIncludeCRLs, OUT DWORD *pcCert, OPTIONAL OUT CERT_BLOB **prgCertBlob, OUT CERT_CONTEXT const ***prgCert, OUT DWORD *pcCRLBlob, OPTIONAL OUT CRL_BLOB **prgCRLBlob, OUT CRL_CONTEXT const ***prgCRL) { HRESULT hr; CERT_CHAIN_PARA ChainParams; CERT_CHAIN_CONTEXT const *pChainContext = NULL; DWORD cElement; CERT_CHAIN_ELEMENT **rgpElement; DWORD cCert; CERT_CONTEXT const **rgpCert = NULL; CERT_CONTEXT const *pccCertLeaf = NULL; CERT_BLOB *rgCertBlob = NULL; DWORD cCRL; CRL_CONTEXT const **rgpCRL = NULL; CRL_BLOB *rgCRLBlob = NULL; DWORD i; DWORD iCert; DWORD iCRL; if (NULL != prgCertBlob) { *prgCertBlob = NULL; } *prgCert = NULL; if (NULL != prgCRLBlob) { *prgCRLBlob = NULL; } *prgCRL = NULL; if (NULL != pbCertLeaf) { pccCertLeaf = CertCreateCertificateContext( X509_ASN_ENCODING, pbCertLeaf, cbCertLeaf); if (NULL == pccCertLeaf) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } } CSASSERT(NULL != pCAContext || NULL != pccCertLeaf); if (NULL == pCAContext || fIncludeCRLs) { // Get the CA cert chain and parent CA CRLs: ZeroMemory(&ChainParams, sizeof(ChainParams)); ChainParams.cbSize = sizeof(ChainParams); //ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; //ChainParams.RequestedUsage.Usage.cUsageIdentifier = 0; //ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = NULL; if (!CertGetCertificateChain( HCCE_LOCAL_MACHINE, // hChainEngine NULL != pCAContext? pCAContext->pccCA : pccCertLeaf, NULL, // pTime NULL, // hAdditionalStore &ChainParams, // pChainPara CERT_CHAIN_REVOCATION_CHECK_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN, NULL, // pvReserved &pChainContext)) // ppChainContext { hr = myHLastError(); _JumpError(hr, error, "CertGetCertificateChain"); } if (0 == pChainContext->cChain || 0 == pChainContext->rgpChain[0]->cElement) { hr = CRYPT_E_NOT_FOUND; _JumpError(hr, error, "No chain"); } cElement = pChainContext->rgpChain[0]->cElement; rgpElement = pChainContext->rgpChain[0]->rgpElement; } else { cElement = pCAContext->cCACertChain; } cCert = cElement; cCRL = 2 * (cCert + 1); // Worst case. *Always* include this CA's CRLs if (NULL != pbCertLeaf) { cCert++; } rgpCert = (CERT_CONTEXT const **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cCert * sizeof(rgpCert[0])); if (NULL == rgpCert) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if (fIncludeCRLs) { rgpCRL = (CRL_CONTEXT const **) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cCRL * sizeof(rgpCRL[0])); if (NULL == rgpCRL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } } iCert = 0; iCRL = 0; // Add parent CA certs and CRLs: if (NULL == pCAContext || fIncludeCRLs) { for (i = 0; i < cElement; i++) { CERT_CHAIN_ELEMENT const *pElement = rgpElement[i]; CERT_REVOCATION_INFO *pRevocationInfo; rgpCert[iCert] = CertDuplicateCertificateContext( pElement->pCertContext); if (NULL != rgpCert[iCert]) { iCert++; } pRevocationInfo = pElement->pRevocationInfo; if (fIncludeCRLs && NULL != pRevocationInfo && CCSIZEOF_STRUCT(CERT_REVOCATION_INFO, pCrlInfo) <= pRevocationInfo->cbSize && NULL != pRevocationInfo->pCrlInfo) { CERT_REVOCATION_CRL_INFO *pCrlInfo; pCrlInfo = pRevocationInfo->pCrlInfo; if (NULL != pCrlInfo) { if (NULL != pCrlInfo->pBaseCrlContext) { rgpCRL[iCRL] = CertDuplicateCRLContext( pCrlInfo->pBaseCrlContext); if (NULL != rgpCRL[iCRL]) { iCRL++; } } if (NULL != pCrlInfo->pDeltaCrlContext) { rgpCRL[iCRL] = CertDuplicateCRLContext( pCrlInfo->pDeltaCrlContext); if (NULL != rgpCRL[iCRL]) { iCRL++; } } } } } } else { for (i = 0; i < pCAContext->cCACertChain; i++) { rgpCert[iCert] = CertDuplicateCertificateContext( pCAContext->apCACertChain[i]); if (NULL != rgpCert[iCert]) { iCert++; } } } if (NULL != pCAContext) { // Add issued cert at the end -- optional Leaf cert: if (NULL != pbCertLeaf) { for (i = 0; i < iCert; i++) { if (cbCertLeaf == rgpCert[i]->cbCertEncoded && 0 == memcmp( pbCertLeaf, rgpCert[i]->pbCertEncoded, cbCertLeaf)) { break; } } if (i == iCert) // if not found in existing array { rgpCert[iCert] = CertDuplicateCertificateContext(pccCertLeaf); if (NULL != rgpCert[iCert]) { iCert++; } } } // Add current CA's Base and delta CRLs: if (fIncludeCRLs) { hr = CRLGetCRL( pCAContext->iKey, FALSE, // fDelta &rgpCRL[iCRL], NULL); // pdwCRLPublishFlags _JumpIfError(hr, error, "CRLGetCRL(base)"); // Base CRL must exist iCRL++; hr = CRLGetCRL( pCAContext->iKey, TRUE, // fDelta &rgpCRL[iCRL], NULL); // pdwCRLPublishFlags _PrintIfError(hr, "CRLGetCRL(delta)"); // Delta CRL might not exist if (S_OK == hr) { iCRL++; } } } CSASSERT(iCert <= cCert); CSASSERT(iCRL <= cCRL); if (NULL != prgCertBlob) { rgCertBlob = (CERT_BLOB *) LocalAlloc( LMEM_FIXED, iCert * sizeof(rgCertBlob[0])); if (NULL == rgCertBlob) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } for (i = 0; i < iCert; i++) { rgCertBlob[i].cbData = rgpCert[i]->cbCertEncoded; rgCertBlob[i].pbData = rgpCert[i]->pbCertEncoded; } } if (NULL != prgCRLBlob && 0 != iCRL) { rgCRLBlob = (CERT_BLOB *) LocalAlloc( LMEM_FIXED, iCRL * sizeof(rgCRLBlob[0])); if (NULL == rgCRLBlob) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } for (i = 0; i < iCRL; i++) { rgCRLBlob[i].cbData = rgpCRL[i]->cbCrlEncoded; rgCRLBlob[i].pbData = rgpCRL[i]->pbCrlEncoded; } } *pcCert = iCert; *prgCert = rgpCert; rgpCert = NULL; if (NULL != prgCertBlob) { *prgCertBlob = rgCertBlob; rgCertBlob = NULL; } *pcCRLBlob = iCRL; *prgCRL = rgpCRL; rgpCRL = NULL; if (NULL != prgCRLBlob) { *prgCRLBlob = rgCRLBlob; rgCRLBlob = NULL; } hr = S_OK; error: pkcsFreeCRLChain(cCert, rgCertBlob, rgpCert, cCRL, rgCRLBlob, rgpCRL); if (NULL != pccCertLeaf) { CertFreeCertificateContext(pccCertLeaf); } if (NULL != pChainContext) { CertFreeCertificateChain(pChainContext); } return(hr); } // Build a PKCS7 CMC response HRESULT PKCSEncodeFullResponse( OPTIONAL IN ICertDBRow *prow, IN CERTSRV_RESULT_CONTEXT const *pResult, IN HRESULT hrRequest, IN WCHAR *pwszDispositionString, OPTIONAL IN CACTX *pCAContext, OPTIONAL IN BYTE const *pbCertLeaf, IN DWORD cbCertLeaf, IN BOOL fIncludeCRLs, OUT BYTE **ppbResponse, // CoTaskMem* OUT DWORD *pcbResponse) { HRESULT hr; CMC_RESPONSE_INFO Response; CMC_STATUS_INFO Status; BYTE *pbContent = NULL; DWORD cbContent; DWORD dwBodyPartIdOfRequest = 1; DWORD dwCMCDataReference = 0; DWORD dwBodyPartId = 1; CMC_TAGGED_ATTRIBUTE aTaggedAttribute[5]; DWORD ita = 0; CRYPT_ATTRIBUTE aAttr[2]; DWORD iAttr = 0; CRYPT_ATTR_BLOB aAttrBlob[7]; DWORD iblob = 0; CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo; CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo; CMC_PEND_INFO PendInfo; DWORD ReqId; DWORD dwRequestFlags; DWORD cb; DWORD i; HCRYPTMSG hMsg = NULL; CERT_CONTEXT const **prgCert = NULL; CRL_CONTEXT const **prgCRL = NULL; CHAR szNonce[(11 + 1) + (8 + 1) * 3]; ZeroMemory(aAttrBlob, sizeof(aAttrBlob)); ZeroMemory(&Status, sizeof(Status)); ZeroMemory(&Response, sizeof(Response)); ZeroMemory(&SignedMsgEncodeInfo, sizeof(SignedMsgEncodeInfo)); SignedMsgEncodeInfo.cbSize = sizeof(SignedMsgEncodeInfo); SignedMsgEncodeInfo.cSigners = 1; SignedMsgEncodeInfo.rgSigners = &SignerEncodeInfo; //SignedMsgEncodeInfo.cCertEncoded = 0; //SignedMsgEncodeInfo.rgCertEncoded = NULL; //SignedMsgEncodeInfo.cCrlEncoded = 0; //SignedMsgEncodeInfo.rgCrlEncoded = NULL; Status.cBodyList = 1; Status.dwOtherInfoChoice = CMC_OTHER_INFO_NO_CHOICE; Status.rgdwBodyList = &dwBodyPartIdOfRequest; Status.pwszStatusString = pwszDispositionString; switch (*pResult->pdwDisposition) { case CR_DISP_ISSUED: case CR_DISP_ISSUED_OUT_OF_BAND: case CR_DISP_REVOKED: // map revoked to CMC_STATUS_FAILED? Status.dwStatus = CMC_STATUS_SUCCESS; break; case CR_DISP_UNDER_SUBMISSION: Status.dwStatus = CMC_STATUS_PENDING; Status.dwOtherInfoChoice = CMC_OTHER_INFO_PEND_CHOICE; Status.pPendInfo = &PendInfo; CSASSERT(NULL != prow); prow->GetRowId(&ReqId); PendInfo.PendToken.cbData = sizeof(ReqId); PendInfo.PendToken.pbData = (BYTE *) &ReqId; cb = sizeof(PendInfo.PendTime); hr = prow->GetProperty( g_wszPropRequestSubmittedWhen, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &PendInfo.PendTime); _JumpIfError(hr, error, "GetProperty"); break; //case CR_DISP_INCOMPLETE: //case CR_DISP_ERROR: //case CR_DISP_DENIED: default: Status.dwStatus = CMC_STATUS_FAILED; if (NULL != prow) { cb = sizeof(hrRequest); hr = prow->GetProperty( g_wszPropRequestStatusCode, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &hrRequest); _JumpIfError(hr, error, "GetProperty(status code)"); } switch (hrRequest) { case CERTSRV_E_BAD_REQUESTSUBJECT: Status.dwFailInfo = CMC_FAIL_BAD_REQUEST; Status.dwOtherInfoChoice = CMC_OTHER_INFO_FAIL_CHOICE; break; } break; } // Encode control attributes for Status, Transaction Id, Sender and // Recipient Nonces and Issued Cert Hash. ZeroMemory(aTaggedAttribute, sizeof(aTaggedAttribute)); // Status: if (!myEncodeObject( X509_ASN_ENCODING, CMC_STATUS, &Status, 0, CERTLIB_USE_LOCALALLOC, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++; aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_STATUS_INFO; aTaggedAttribute[ita].Attribute.cValue = 1; aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob]; iblob++; ita++; // Transaction Id: if (pResult->fTransactionId) { if (!myEncodeObject( X509_ASN_ENCODING, X509_INTEGER, &pResult->dwTransactionId, 0, CERTLIB_USE_LOCALALLOC, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++; aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_TRANSACTION_ID; aTaggedAttribute[ita].Attribute.cValue = 1; aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob]; iblob++; ita++; } if (NULL != pResult->pbSenderNonce && 0 != pResult->cbSenderNonce) { CRYPT_DATA_BLOB Blob; FILETIME ft; DWORD dw; DWORD cch; // Recipient Nonce: Blob.pbData = const_cast(pResult->pbSenderNonce); Blob.cbData = pResult->cbSenderNonce; if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &Blob, 0, CERTLIB_USE_LOCALALLOC, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++; aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_RECIPIENT_NONCE; aTaggedAttribute[ita].Attribute.cValue = 1; aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob]; iblob++; ita++; // Sender Nonce: GetSystemTimeAsFileTime(&ft); dw = GetTickCount(); cch = sprintf( szNonce, "%u %08lx %08lx-%08lx", *pResult->pdwRequestId, dw, ft.dwHighDateTime, ft.dwLowDateTime); CSASSERT(ARRAYSIZE(szNonce) > cch); Blob.pbData = (BYTE *) szNonce; Blob.cbData = cch; if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &Blob, 0, CERTLIB_USE_LOCALALLOC, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } aTaggedAttribute[ita].dwBodyPartID = dwBodyPartId++; aTaggedAttribute[ita].Attribute.pszObjId = szOID_CMC_SENDER_NONCE; aTaggedAttribute[ita].Attribute.cValue = 1; aTaggedAttribute[ita].Attribute.rgValue = &aAttrBlob[iblob]; iblob++; ita++; } // Issued Cert Hash: if (NULL != pbCertLeaf) { CSASSERT(NULL != prow); hr = pkcsGetHashAsOctet( prow, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData); _JumpIfError(hr, error, "pkcsGetHashAsOctet"); aAttr[iAttr].pszObjId = szOID_ISSUED_CERT_HASH; aAttr[iAttr].cValue = 1; aAttr[iAttr].rgValue = &aAttrBlob[iblob]; iblob++; iAttr++; } // Computed hash of private key encrypted to this CA, for client // confirmation. if (NULL != pResult->pbKeyHashOut) { CRYPT_DATA_BLOB Blob; Blob.pbData = pResult->pbKeyHashOut; Blob.cbData = pResult->cbKeyHashOut; if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &Blob, 0, CERTLIB_USE_LOCALALLOC, &aAttrBlob[iblob].pbData, &aAttrBlob[iblob].cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } aAttr[iAttr].pszObjId = szOID_ENCRYPTED_KEY_HASH; aAttr[iAttr].cValue = 1; aAttr[iAttr].rgValue = &aAttrBlob[iblob]; iblob++; iAttr++; } if (0 != iAttr) { hr = BuildCMCAttributes( iAttr, // cAttribute aAttr, // rgAttribute dwCMCDataReference, dwBodyPartIdOfRequest, dwBodyPartId++, &aTaggedAttribute[ita], &aAttrBlob[iblob]); _JumpIfError(hr, error, "BuildCMCAttributes"); iblob++; ita++; } CSASSERT(ARRAYSIZE(aTaggedAttribute) >= ita); CSASSERT(ARRAYSIZE(aAttr) >= iAttr); CSASSERT(ARRAYSIZE(aAttrBlob) >= iblob); Response.cTaggedAttribute = ita; Response.rgTaggedAttribute = aTaggedAttribute; if (!myEncodeObject( X509_ASN_ENCODING, CMC_RESPONSE, &Response, 0, CERTLIB_USE_LOCALALLOC, &pbContent, &cbContent)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } if (NULL == pCAContext) { pCAContext = g_pCAContextCurrent; } ZeroMemory(&SignerEncodeInfo, sizeof(SignerEncodeInfo)); SignerEncodeInfo.cbSize = sizeof(SignerEncodeInfo); SignerEncodeInfo.pCertInfo = pCAContext->pccCA->pCertInfo; SignerEncodeInfo.hCryptProv = pCAContext->hProvCA; SignerEncodeInfo.dwKeySpec = AT_SIGNATURE; SignerEncodeInfo.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1; //SignerEncodeInfo.pvHashAuxInfo = NULL; //SignerEncodeInfo.cAuthAttr = 0; //SignerEncodeInfo.rgAuthAttr = NULL; //SignerEncodeInfo.cUnauthAttr = 0; //SignerEncodeInfo.rgUnauthAttr = NULL; if (NULL != pbCertLeaf) { hr = pkcsBuildCRLChain( pCAContext, pbCertLeaf, cbCertLeaf, fIncludeCRLs, &SignedMsgEncodeInfo.cCertEncoded, &SignedMsgEncodeInfo.rgCertEncoded, &prgCert, &SignedMsgEncodeInfo.cCrlEncoded, &SignedMsgEncodeInfo.rgCrlEncoded, &prgCRL); _JumpIfError(hr, error, "pkcsBuildCRLChain"); } dwRequestFlags = 0; if (NULL != prow) { cb = sizeof(dwRequestFlags); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &dwRequestFlags); _JumpIfError(hr, error, "GetProperty"); } #define szOID_CT_PKI_RESPONSE_OLDRFC "1.3.6.1.5.5.7.5.3" // BUGBUG: temporary! hMsg = CryptMsgOpenToEncode( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, CMSG_CMS_ENCAPSULATED_CONTENT_FLAG, // dwFlags CMSG_SIGNED, &SignedMsgEncodeInfo, (CR_FLG_OLDRFCCMC & dwRequestFlags)? szOID_CT_PKI_RESPONSE_OLDRFC : szOID_CT_PKI_RESPONSE, NULL); // pStreamInfo if (NULL == hMsg) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgOpenToEncode"); } if (!CryptMsgUpdate(hMsg, pbContent, cbContent, TRUE)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgUpdate"); } // Return the encoded and signed content. // Use CMSG_CONTENT_PARAM to get the signed message. hr = myCryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, CERTLIB_USE_COTASKMEMALLOC, (VOID **) ppbResponse, pcbResponse); _JumpIfError(hr, error, "myCryptMsgGetParam"); error: pkcsFreeCRLChain( SignedMsgEncodeInfo.cCertEncoded, SignedMsgEncodeInfo.rgCertEncoded, prgCert, SignedMsgEncodeInfo.cCrlEncoded, SignedMsgEncodeInfo.rgCrlEncoded, prgCRL); for (i = 0; i < ARRAYSIZE(aAttrBlob); i++) { if (NULL != aAttrBlob[i].pbData) { LocalFree(aAttrBlob[i].pbData); } } if (NULL != hMsg) { CryptMsgClose(hMsg); } if (NULL != pbContent) { LocalFree(pbContent); } return(hr); } // Build a PKCS7 NULL signature with encapsulated certs HRESULT pkcsEncodeCertChain( OPTIONAL IN CACTX *pCAContext, OPTIONAL IN BYTE const *pbCertLeaf, IN DWORD cbCertLeaf, IN BYTE const *pbToBeSigned, IN DWORD cbToBeSigned, IN BOOL fIncludeCRLs, OUT BYTE **ppbCertChain, // CoTaskMem* OUT DWORD *pcbCertChain) { HRESULT hr; CRYPT_SIGN_MESSAGE_PARA csmp; CRYPT_ALGORITHM_IDENTIFIER DigestAlgorithm = { szOID_OIWSEC_sha1, 0, 0 }; // init csmp for empty signature ZeroMemory(&csmp, sizeof(csmp)); csmp.cbSize = sizeof(csmp); csmp.dwMsgEncodingType = PKCS_7_ASN_ENCODING; //csmp.pSigningCert = NULL; csmp.HashAlgorithm = DigestAlgorithm; //csmp.cMsgCert = 0; //csmp.rgpMsgCert = NULL; //csmp.cMsgCrl = 0; //csmp.rgpMsgCrl = NULL; hr = pkcsBuildCRLChain( pCAContext, pbCertLeaf, cbCertLeaf, fIncludeCRLs, &csmp.cMsgCert, NULL, &csmp.rgpMsgCert, &csmp.cMsgCrl, NULL, &csmp.rgpMsgCrl); _JumpIfError(hr, error, "pkcsBuildCRLChain"); if (!myCryptSignMessage( &csmp, pbToBeSigned, cbToBeSigned, CERTLIB_USE_COTASKMEMALLOC, ppbCertChain, pcbCertChain)) { hr = myHLastError(); _JumpError(hr, error, "myCryptSignMessage"); } hr = S_OK; error: pkcsFreeCRLChain( csmp.cMsgCert, NULL, csmp.rgpMsgCert, csmp.cMsgCrl, NULL, csmp.rgpMsgCrl); return(hr); } HRESULT PKCSGetCACert( IN DWORD iCert, OUT BYTE **ppbCACert, OUT DWORD *pcbCACert) { HRESULT hr; DWORD State; CACTX *pCAContext; hr = PKCSMapCertIndex(iCert, &iCert, &State); _JumpIfError(hr, error, "PKCSMapCertIndex"); // Now we know iCert is a valid Cert Index: pCAContext = &g_aCAContext[iCert]; if (NULL == pCAContext->pccCA) { hr = E_INVALIDARG; _JumpError(hr, error, "invalid cert"); } *pcbCACert = pCAContext->pccCA->cbCertEncoded; *ppbCACert = pCAContext->pccCA->pbCertEncoded; error: return(hr); } HRESULT PKCSGetCAChain( IN DWORD iCert, IN BOOL fIncludeCRLs, OUT BYTE **ppbCAChain, // CoTaskMem* OUT DWORD *pcbCAChain) { HRESULT hr; DWORD State; CACTX *pCAContext; hr = PKCSMapCertIndex(iCert, &iCert, &State); _JumpIfError(hr, error, "PKCSMapCertIndex"); // Now we know iCert is a valid Cert Index: pCAContext = &g_aCAContext[iCert]; if (NULL == pCAContext->pccCA) { hr = E_INVALIDARG; _JumpError(hr, error, "invalid cert"); } hr = pkcsEncodeCertChain( pCAContext, NULL, // pbCertLeaf 0, // cbCertLeaf pCAContext->pccCA->pbCertEncoded, // pbToBeSigned pCAContext->pccCA->cbCertEncoded, // cbToBeSigned fIncludeCRLs, ppbCAChain, // CoTaskMem* pcbCAChain); _JumpIfError(hr, error, "PKCSEncodeCertChain"); error: return(hr); } HRESULT pkcsFormXchgKeyContainerName( IN DWORD dwRequestId, OUT WCHAR **ppwszKeyContainer) { HRESULT hr; DWORD cwcSuffix; DWORD cwcName; WCHAR wszSuffix[32]; WCHAR wszKeyContainer[MAX_PATH]; *ppwszKeyContainer = NULL; cwcSuffix = wsprintf(wszSuffix, L"%ws(%u)", g_wszCNXchgSuffix, dwRequestId); CSASSERT(ARRAYSIZE(wszSuffix) > cwcSuffix); cwcName = wcslen(g_wszSanitizedName); if (cwcName > MAX_PATH - cwcSuffix) { cwcName = MAX_PATH - cwcSuffix; } CSASSERT(ARRAYSIZE(wszKeyContainer) > cwcName); wcscpy(wszKeyContainer, g_wszSanitizedName); wcscpy(&wszKeyContainer[cwcName], wszSuffix); hr = myDupString(wszKeyContainer, ppwszKeyContainer); _JumpIfError(hr, error, "myDupString"); DBGPRINT(( DBG_SS_CERTSRV, "pkcsFormXchgKeyContainerName: %ws\n", *ppwszKeyContainer)); error: return(hr); } HRESULT pkcsAcquireKey( OPTIONAL IN WCHAR const *pwszKeyContainer, OUT HCRYPTPROV *phProv) { HRESULT hr; *phProv = NULL; if (!CryptAcquireContext( phProv, pwszKeyContainer, g_pwszXchgProvName, g_dwXchgProvType, g_fXchgMachineKeyset? CRYPT_MACHINE_KEYSET : 0)) { hr = myHLastError(); _JumpError(hr, error, "CryptAcquireContext"); } hr = S_OK; error: return(hr); } VOID pkcsDeleteKey( OPTIONAL IN WCHAR const *pwszKeyContainer) { HRESULT hr; HCRYPTPROV hProv; if (NULL != pwszKeyContainer) { if (!CryptAcquireContext( &hProv, pwszKeyContainer, g_pwszXchgProvName, g_dwXchgProvType, CRYPT_DELETEKEYSET | (g_fXchgMachineKeyset? CRYPT_MACHINE_KEYSET : 0))) { hr = myHLastError(); _JumpError(hr, error, "CryptAcquireContext"); } } error: ; } VOID pkcsLoadCAXchgCSPInfo( IN BOOL fSetDefaults) { HRESULT hr = S_FALSE; if (NULL != g_pwszXchgProvName) { LocalFree(g_pwszXchgProvName); g_pwszXchgProvName = NULL; } if (!fSetDefaults) { hr = myGetCertSrvCSP( TRUE, // fEncryptionCSP g_wszSanitizedName, &g_dwXchgProvType, &g_pwszXchgProvName, &g_XchgidAlg, &g_fXchgMachineKeyset, &g_dwXchgKeySize); if (S_OK != hr) { _PrintError(hr, "myGetCertSrvCSP(CAXchg)"); } } if (S_OK != hr) { g_dwXchgProvType = PROV_RSA_FULL; g_pwszXchgProvName = NULL; g_XchgidAlg = CALG_3DES; g_fXchgMachineKeyset = TRUE; g_dwXchgKeySize = 0; } if (0 == g_dwXchgKeySize) { g_dwXchgKeySize = 1024; } } HRESULT pkcsCreateNewCAXchgCert( IN WCHAR const *pwszUserName) { HRESULT hr; ICertDBRow *prow = NULL; DWORD dwRequestFlags = CR_FLG_CAXCHGCERT; BOOL fSubjectNameSet; BOOL fErrorLogged; CERT_PUBLIC_KEY_INFO *pPubKey = NULL; DWORD cb; CAXCHGCTX CAXchgContext; CAXCHGCTX *rgCAXchgContext; CERT_EXTENSION aExt[4]; DWORD cExt; DWORD i; CERTTRANSBLOB ctbCert; // CoTaskMem* CERTSRV_RESULT_CONTEXT Result; WCHAR *pwszDisposition = NULL; WCHAR *pwszMachineRequesterName = NULL; BOOL fCommitted = FALSE; static char *s_apszObjId[] = { szOID_KP_CA_EXCHANGE, }; ZeroMemory(&CAXchgContext, sizeof(CAXchgContext)); ZeroMemory(&aExt, sizeof(aExt)); ZeroMemory(&ctbCert, sizeof(ctbCert)); hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, &prow); _JumpIfError(hr, error, "OpenRow"); prow->GetRowId(&CAXchgContext.ReqId); hr = myGetComputerObjectName(NameSamCompatible, &pwszMachineRequesterName); if (S_OK != hr) { _PrintError(hr, "myGetComputerObjectName"); hr = myGetUserNameEx(NameSamCompatible, &pwszMachineRequesterName); _JumpIfError(hr, error, "myGetUserNameEx"); } hr = prow->SetProperty( g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszMachineRequesterName); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( g_wszPropCallerName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszUserName); _JumpIfError(hr, error, "SetProperty"); hr = prow->SetProperty( wszPROPCERTIFICATETEMPLATE, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, MAXDWORD, (BYTE const *) wszCERTTYPE_CA_EXCHANGE); _JumpIfError(hr, error, "SetProperty"); hr = pkcsFormXchgKeyContainerName( CAXchgContext.ReqId, &CAXchgContext.pwszKeyContainerName); _JumpIfError(hr, error, "pkcsFormXchgKeyContainerName"); for (i = 0; ; i++) { hr = myGenerateKeys( CAXchgContext.pwszKeyContainerName, g_pwszXchgProvName, g_fXchgMachineKeyset, AT_KEYEXCHANGE, g_dwXchgProvType, g_dwXchgKeySize, &CAXchgContext.hProvCA); if (S_OK == hr) { break; } _PrintErrorStr(hr, "myGenerateKeys", g_pwszXchgProvName); LogEventHResult( EVENTLOG_ERROR_TYPE, NULL == g_pwszXchgProvName? MSG_E_BAD_DEFAULT_CA_XCHG_CSP : MSG_E_BAD_REGISTRY_CA_XCHG_CSP, hr); if (0 != i || NULL == g_pwszXchgProvName) { _JumpError(hr, error, "myGenerateKeys"); } pkcsLoadCAXchgCSPInfo(TRUE); // switch to default CSP } if (0 != i) { hr = LogEvent( EVENTLOG_WARNING_TYPE, MSG_E_USE_DEFAULT_CA_XCHG_CSP, 0, // cpwsz NULL); // apwsz _PrintIfError(hr, "LogEvent"); } if (!myCryptExportPublicKeyInfo( CAXchgContext.hProvCA, AT_KEYEXCHANGE, CERTLIB_USE_LOCALALLOC, &pPubKey, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myCryptExportPublicKeyInfo"); } hr = PropSetRequestTimeProperty(prow, g_wszPropRequestSubmittedWhen); _JumpIfError(hr, error, "PropSetRequestTimeProperty"); hr = CoreSetDisposition(prow, DB_DISP_ACTIVE); _JumpIfError(hr, error, "CoreSetDisposition"); hr = pkcsSetRequestNameInfo( prow, &g_pCAContextCurrent->pccCA->pCertInfo->Subject, g_wszCNXchgSuffix, &dwRequestFlags, &fSubjectNameSet); _JumpIfError(hr, error, "pkcsSetRequestNameInfo"); hr = prow->SetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwRequestFlags), (BYTE const *) &dwRequestFlags); _JumpIfError(hr, error, "SetProperty(RequestFlags)"); CSASSERT(fSubjectNameSet); hr = pkcsSetPublicKeyProperties(prow, pPubKey); _JumpIfError(hr, error, "pkcsSetPublicKeyProperties"); hr = prow->CopyRequestNames(); _JumpIfError(hr, error, "CopyRequestNames"); hr = PKCSSetServerProperties( prow, g_lCAXchgValidityPeriodCount, g_enumCAXchgValidityPeriod); _JumpIfError(hr, error, "PKCSSetServerProperties"); cExt = 0; // szOID_KEY_USAGE { CRYPT_BIT_BLOB KeyUsage; BYTE abKeyUsage[1] = { CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_KEY_AGREEMENT_KEY_USAGE }; KeyUsage.pbData = abKeyUsage; KeyUsage.cbData = sizeof(abKeyUsage); KeyUsage.cUnusedBits = 0; if (!myEncodeKeyUsage( X509_ASN_ENCODING, &KeyUsage, CERTLIB_USE_LOCALALLOC, &aExt[cExt].Value.pbData, &aExt[cExt].Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeKeyUsage"); } } hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, TEXT(szOID_KEY_USAGE), 0, // ExtFlags aExt[cExt].Value.cbData, aExt[cExt].Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); cExt++; // szOID_ENHANCED_KEY_USAGE { CERT_ENHKEY_USAGE eku; eku.cUsageIdentifier = ARRAYSIZE(s_apszObjId); eku.rgpszUsageIdentifier = s_apszObjId; if (!myEncodeObject( X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &eku, 0, CERTLIB_USE_LOCALALLOC, &aExt[cExt].Value.pbData, &aExt[cExt].Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } } hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, TEXT(szOID_ENHANCED_KEY_USAGE), 0, // ExtFlags aExt[cExt].Value.cbData, aExt[cExt].Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); cExt++; // szOID_APPLICATION_CERT_POLICIES { CERT_POLICY_INFO acpi[ARRAYSIZE(s_apszObjId)]; CERT_POLICIES_INFO cps; ZeroMemory(&acpi, sizeof(acpi)); cps.cPolicyInfo = ARRAYSIZE(s_apszObjId); cps.rgPolicyInfo = acpi; for (i = 0; i < ARRAYSIZE(s_apszObjId); i++) { acpi[i].pszPolicyIdentifier = s_apszObjId[i]; } if (!myEncodeObject( X509_ASN_ENCODING, X509_CERT_POLICIES, &cps, 0, CERTLIB_USE_LOCALALLOC, &aExt[cExt].Value.pbData, &aExt[cExt].Value.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } } hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, TEXT(szOID_APPLICATION_CERT_POLICIES), 0, // ExtFlags aExt[cExt].Value.cbData, aExt[cExt].Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); cExt++; // szOID_ENROLL_CERTTYPE_EXTENSION hr = myBuildCertTypeExtension(wszCERTTYPE_CA_EXCHANGE, &aExt[cExt]); _JumpIfError(hr, error, "myBuildCertTypeExtension"); hr = PropSetExtension( prow, PROPTYPE_BINARY | PROPCALLER_SERVER, TEXT(szOID_ENROLL_CERTTYPE_EXTENSION), 0, // ExtFlags aExt[cExt].Value.cbData, aExt[cExt].Value.pbData); _JumpIfError(hr, error, "PropSetExtension"); cExt++; CSASSERT(cExt == ARRAYSIZE(aExt)); ZeroMemory(&Result, sizeof(Result)); Result.pctbCert = &ctbCert; hr = PKCSCreateCertificate( prow, DB_DISP_ISSUED, FALSE, &fErrorLogged, NULL, &Result); _JumpIfError(hr, error, "PKCSCreateCertificate"); CAXchgContext.pccCA = CertCreateCertificateContext( X509_ASN_ENCODING, ctbCert.pb, ctbCert.cb); if (NULL == CAXchgContext.pccCA) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } if (NULL == g_aCAXchgContext) { CSASSERT(0 == g_cCAXchgCerts); rgCAXchgContext = (CAXCHGCTX *) LocalAlloc( LMEM_FIXED, sizeof(rgCAXchgContext[0])); } else { rgCAXchgContext = (CAXCHGCTX *) LocalReAlloc( g_aCAXchgContext, (g_cCAXchgCerts + 1) * sizeof(rgCAXchgContext[0]), LMEM_MOVEABLE); } if (NULL == rgCAXchgContext) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc/ReAlloc"); } g_aCAXchgContext = rgCAXchgContext; g_aCAXchgContext[g_cCAXchgCerts] = CAXchgContext; pwszDisposition = CoreBuildDispositionString( g_pwszRequestedBy, pwszUserName, NULL, NULL, S_OK, FALSE); hr = CoreSetRequestDispositionFields( prow, S_OK, DB_DISP_ISSUED, pwszDisposition); _JumpIfError(hr, error, "CoreSetRequestDispositionFields"); hr = prow->CommitTransaction(TRUE); _JumpIfError(hr, error, "CommitTransaction"); fCommitted = TRUE; g_pCAXchgContextCurrent = &g_aCAXchgContext[g_cCAXchgCerts]; g_cCAXchgCerts++; ZeroMemory(&CAXchgContext, sizeof(CAXchgContext)); error: if (NULL != pwszMachineRequesterName) { LocalFree(pwszMachineRequesterName); } if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } for (i = 0; i < ARRAYSIZE(aExt); i++) { if (NULL != aExt[i].Value.pbData) { LocalFree(aExt[i].Value.pbData); } } if (NULL != prow) { if (S_OK != hr && !fCommitted) { HRESULT hr2 = prow->CommitTransaction(FALSE); _PrintIfError(hr2, "CommitTransaction"); } prow->Release(); } if (NULL != ctbCert.pb) { CoTaskMemFree(ctbCert.pb); } if (NULL != pPubKey) { LocalFree(pPubKey); } if (NULL != CAXchgContext.pccCA) { CertFreeCertificateContext(CAXchgContext.pccCA); } if (NULL != CAXchgContext.hProvCA) { CryptReleaseContext(CAXchgContext.hProvCA, 0); pkcsDeleteKey(CAXchgContext.pwszKeyContainerName); } if (NULL != CAXchgContext.pwszKeyContainerName) { LocalFree(CAXchgContext.pwszKeyContainerName); } return(hr); } VOID pkcsReleaseCAXchgContext( IN OUT CAXCHGCTX *pCAXchgContext) { if (NULL != pCAXchgContext->hProvCA) { CryptReleaseContext(pCAXchgContext->hProvCA, 0); pCAXchgContext->hProvCA = NULL; } if (NULL != pCAXchgContext->pccCA) { CertFreeCertificateContext(pCAXchgContext->pccCA); pCAXchgContext->pccCA = NULL; } if (NULL != pCAXchgContext->pwszKeyContainerName) { LocalFree(pCAXchgContext->pwszKeyContainerName); pCAXchgContext->pwszKeyContainerName = NULL; } } VOID pkcsReleaseCAXchgContextArray() { DWORD i; if (NULL != g_aCAXchgContext) { for (i = 0; i < g_cCAXchgCerts; i++) { pkcsReleaseCAXchgContext(&g_aCAXchgContext[i]); } LocalFree(g_aCAXchgContext); g_aCAXchgContext = NULL; } g_cCAXchgCerts = 0; g_pCAContextCurrent = NULL; } HRESULT pkcsLoadCAXchgContext( IN DWORD iHash) { HRESULT hr; CAXCHGCTX *pCAXchgContext; DWORD dwRequestFlags; DWORD NameId; HCRYPTPROV hProv = NULL; WCHAR *pwszKeyContainer = NULL; WCHAR *pwszHash = NULL; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; BYTE *pbHash = NULL; DWORD cbHash; BYTE *pbCert = NULL; DWORD cbCert; DWORD cb; BSTR strHash = NULL; ICertDBRow *prow = NULL; DWORD dwRequestId; CERT_CONTEXT const *pcc = NULL; BOOL fDeleteKey = FALSE; DWORD i; hr = myGetCARegHash( g_wszSanitizedName, CSRH_CAXCHGCERT, iHash, &pbHash, &cbHash); _JumpIfError2(hr, error, "myGetCARegHash", S_FALSE); hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); DBGPRINT(( DBG_SS_CERTSRV, "Reloading Xchg CAContext[%u]:\n %ws\n", iHash, strHash)); hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPOPEN_CERTHASH | PROPTABLE_REQCERT, 0, strHash, &prow); _JumpIfError(hr, error, "OpenRow(xchg cert)"); prow->GetRowId(&dwRequestId); hr = pkcsFormXchgKeyContainerName(dwRequestId, &pwszKeyContainer); _JumpIfError(hr, error, "pkcsFormXchgKeyContainerName"); cb = sizeof(dwRequestFlags); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &dwRequestFlags); _JumpIfError(hr, error, "GetProperty(RequestFlags)"); if (0 == (CR_FLG_CAXCHGCERT & dwRequestFlags)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Not a CA Xchg cert"); } cb = sizeof(NameId); hr = prow->GetProperty( g_wszPropCertificateIssuerNameID, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cb, (BYTE *) &NameId); _JumpIfError(hr, error, "GetProperty"); hr = PKCSGetProperty( prow, g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbCert, (BYTE **) &pbCert); _JumpIfError(hr, error, "PKCSGetProperty(xchg cert)"); pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert); if (NULL == pcc) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } for (i = 0; ; i++) { hr = pkcsAcquireKey(pwszKeyContainer, &hProv); _PrintIfErrorStr(hr, "pkcsAcquireKey", g_pwszXchgProvName); if (S_OK == hr) { hr = myValidateKeyForEncrypting( hProv, &pcc->pCertInfo->SubjectPublicKeyInfo, CALG_3DES); _PrintIfErrorStr(hr, "myValidateKeyForEncrypting", g_pwszXchgProvName); } if (S_OK == hr) { break; } LogEventHResult( EVENTLOG_ERROR_TYPE, NULL == g_pwszXchgProvName? MSG_E_BAD_DEFAULT_CA_XCHG_CSP : MSG_E_BAD_REGISTRY_CA_XCHG_CSP, hr); if (0 != i || NULL == g_pwszXchgProvName) { fDeleteKey = TRUE; _JumpError(hr, error, "pkcsAcquireKey/myValidateKeyForEncrypting"); } pkcsLoadCAXchgCSPInfo(TRUE); // switch to default CSP } if (0 != i) { hr = LogEvent( EVENTLOG_WARNING_TYPE, MSG_E_USE_DEFAULT_CA_XCHG_CSP, 0, // cpwsz NULL); // apwsz _PrintIfError(hr, "LogEvent"); } hr = pkcsVerifyCertContext(NULL, FALSE, pcc); if (S_OK != hr) { fDeleteKey = TRUE; _JumpErrorStr(hr, error, "pkcsVerifyCertContext", L"CAXchg cert invalid"); } pCAXchgContext = &g_aCAXchgContext[g_cCAXchgCerts]; ZeroMemory(pCAXchgContext, sizeof(*pCAXchgContext)); pCAXchgContext->ReqId = dwRequestId; pCAXchgContext->pccCA = pcc; pcc = NULL; pCAXchgContext->hProvCA = hProv; hProv = NULL; pCAXchgContext->pwszKeyContainerName = pwszKeyContainer; pwszKeyContainer = NULL; pCAXchgContext->iCertSig = CANAMEIDTOICERT(NameId); g_cCAXchgCerts++; hr = S_OK; error: if (NULL != hProv) { CryptReleaseContext(hProv, 0); } if (fDeleteKey) { pkcsDeleteKey(pwszKeyContainer); } if (NULL != prow) { prow->Release(); } if (NULL != pbCert) { LocalFree(pbCert); } if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (NULL != pwszKeyContainer) { LocalFree(pwszKeyContainer); } if (NULL != pbHash) { LocalFree(pbHash); } if (NULL != strHash) { SysFreeString(strHash); } return(hr); } HRESULT pkcsLoadCAXchgContextArray( OUT BOOL *pfIncompleteLoad) { HRESULT hr; DWORD cCAXchgCerts; DWORD iHash; DWORD i; // get provider name, etc. pkcsLoadCAXchgCSPInfo(FALSE); // find & load CA Xchg certs, etc. *pfIncompleteLoad = TRUE; hr = myGetCARegHashCount( g_wszSanitizedName, CSRH_CAXCHGCERT, &cCAXchgCerts); if (S_OK == hr && 0 == cCAXchgCerts) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } _JumpIfError(hr, error, "myGetCARegHashCount"); g_aCAXchgContext = (CAXCHGCTX *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cCAXchgCerts * sizeof(g_aCAXchgContext[0])); if (NULL == g_aCAXchgContext) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } for (iHash = 0; iHash < cCAXchgCerts; iHash++) { hr = pkcsLoadCAXchgContext(iHash); _PrintIfError(hr, "pkcsLoadCAXchgContext"); } if (0 == g_cCAXchgCerts) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); _JumpError(hr, error, "g_cCAXchgCerts"); } g_pCAXchgContextCurrent = &g_aCAXchgContext[0]; for (i = 1; i < g_cCAXchgCerts; i++) { if (0 < CompareFileTime( &g_aCAXchgContext[i].pccCA->pCertInfo->NotAfter, &g_pCAXchgContextCurrent->pccCA->pCertInfo->NotAfter)) { g_pCAXchgContextCurrent = &g_aCAXchgContext[i]; } } if (cCAXchgCerts == g_cCAXchgCerts) { *pfIncompleteLoad = FALSE; } hr = S_OK; error: if (S_OK != hr) { if (NULL != g_aCAXchgContext) { LocalFree(g_aCAXchgContext); g_aCAXchgContext = NULL; } g_cCAXchgCerts = 0; g_pCAXchgContextCurrent = NULL; } return(hr); } HRESULT pkcsUpdateCAXchgStoreAndRegistry( IN BOOL fUpdateRegistry) { HRESULT hr; DWORD i; DWORD iHash; CAXCHGCTX *pCAXchgContext; HCERTSTORE hStore = NULL; CERT_KEY_CONTEXT ckc; CERT_CONTEXT const *pccStore = NULL; hStore = CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, // hProv 0, // dwFlags NULL); // pvPara if (NULL == hStore) { hr = myHLastError(); _JumpError(hr, error, "CertOpenStore"); } if (fUpdateRegistry) { hr = myDeleteCertRegValue( g_wszSanitizedName, NULL, NULL, g_wszRegCAXchgCertHash); _PrintIfError(hr, "myDeleteCertRegValue"); } ZeroMemory(&ckc, sizeof(ckc)); ckc.cbSize = sizeof(ckc); ckc.dwKeySpec = AT_KEYEXCHANGE; iHash = 0; for (i = 0; i < g_cCAXchgCerts; i++) { pCAXchgContext = &g_aCAXchgContext[i]; if (CTXF_EXPIRED & pCAXchgContext->Flags) { continue; } // Add as encoded blob to avoid all properties, key prov info, etc. if (!CertAddEncodedCertificateToStore( hStore, X509_ASN_ENCODING, pCAXchgContext->pccCA->pbCertEncoded, pCAXchgContext->pccCA->cbCertEncoded, CERT_STORE_ADD_REPLACE_EXISTING, &pccStore)) // ppCertContext { hr = myHLastError(); _JumpError(hr, error, "CertAddEncodedCertificateToStore"); } ckc.hCryptProv = pCAXchgContext->hProvCA; if (!CertSetCertificateContextProperty( pccStore, CERT_KEY_CONTEXT_PROP_ID, CERT_STORE_NO_CRYPT_RELEASE_FLAG, &ckc)) { hr = myHLastError(); _JumpError(hr, error, "CertSetCertificateContextProperty"); } CertFreeCertificateContext(pccStore); pccStore = NULL; DBGPRINT(( DBG_SS_CERTSRV, "Add to CA Xchg memory store: '%ws'\n", pCAXchgContext->pwszKeyContainerName)); if (fUpdateRegistry) { hr = mySetCARegHash( g_wszSanitizedName, CSRH_CAXCHGCERT, iHash, pCAXchgContext->pccCA); if (S_OK != hr) { _PrintError(hr, "mySetCARegHash"); continue; } } iHash++; } if (NULL != g_hStoreCAXchg) { CertCloseStore(g_hStoreCAXchg, CERT_CLOSE_STORE_CHECK_FLAG); } g_hStoreCAXchg = hStore; hStore = NULL; hr = S_OK; error: if (NULL != pccStore) { CertFreeCertificateContext(pccStore); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } return(hr); } HRESULT PKCSIsRevoked( IN DWORD RequestId, OPTIONAL IN WCHAR const *pwszSerialNumber, OUT LONG *pRevocationReason, OUT LONG *pDisposition) { HRESULT hr; ICertDBRow *prow = NULL; BYTE *pbHash = NULL; DWORD cbHash; BSTR strHash = NULL; DWORD Disposition; DWORD cbProp; FILETIME ftRevoked; FILETIME ftCurrent; *pRevocationReason = CRL_REASON_UNSPECIFIED; *pDisposition = CA_DISP_INVALID; hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPTABLE_REQCERT, RequestId, pwszSerialNumber, &prow); _PrintIfErrorStr2(hr, "OpenRow", pwszSerialNumber, CERTSRV_E_PROPERTY_EMPTY); if (CERTSRV_E_PROPERTY_EMPTY == hr && NULL != pwszSerialNumber) { _PrintErrorStr2( hr, "OpenRow(serial)", pwszSerialNumber, CERTSRV_E_PROPERTY_EMPTY); hr = WszToMultiByteInteger(TRUE, pwszSerialNumber, &cbHash, &pbHash); _JumpIfError(hr, error, "WszToMultiByteInteger"); hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash); _JumpIfError(hr, error, "MultiByteIntegerToBstr"); hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPOPEN_CERTHASH | PROPTABLE_REQCERT, RequestId, strHash, &prow); _PrintIfErrorStr2(hr, "OpenRow", strHash, CERTSRV_E_PROPERTY_EMPTY); } if (S_OK != hr) { if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = S_OK; // disposition indicates cert is invalid } goto error; } cbProp = sizeof(Disposition); hr = prow->GetProperty( g_wszPropRequestDisposition, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &Disposition); _JumpIfError(hr, error, "GetProperty(Disposition)"); if (DB_DISP_ISSUED == Disposition || (DB_DISP_CA_CERT == Disposition && IsRootCA(g_CAType))) { *pDisposition = CA_DISP_VALID; goto error; } if (DB_DISP_REVOKED != Disposition) { goto error; } cbProp = sizeof(ftRevoked); hr = prow->GetProperty( g_wszPropRequestRevokedEffectiveWhen, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &ftRevoked); if (CERTSRV_E_PROPERTY_EMPTY == hr) { *pDisposition = CA_DISP_VALID; hr = S_OK; goto error; } _JumpIfError(hr, error, "GetProperty(RevokedEffectiveWhen)"); GetSystemTimeAsFileTime(&ftCurrent); if (0 < CompareFileTime(&ftRevoked, &ftCurrent)) { *pDisposition = CA_DISP_VALID; goto error; } cbProp = sizeof(*pRevocationReason); hr = prow->GetProperty( g_wszPropRequestRevokedReason, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) pRevocationReason); _JumpIfError(hr, error, "GetProperty(RevokedReason)"); *pDisposition = CA_DISP_REVOKED; error: if (NULL != pbHash) { LocalFree(pbHash); } if (NULL != strHash) { SysFreeString(strHash); } if (NULL != prow) { prow->Release(); } return(hr); } HRESULT PKCSGetCAXchgCert( IN DWORD iCert, IN WCHAR const *pwszUserName, OUT DWORD *piCertSig, OUT BYTE **ppbCACert, OUT DWORD *pcbCACert) { HRESULT hr; DWORD State; BOOL fNewCert = FALSE; BOOL fIncompleteLoad = FALSE; FILETIME ft; if (MAXDWORD != iCert && 0 != iCert) { hr = E_INVALIDARG; _JumpError(hr, error, "bad Xchg CertIndex"); } if (NULL == g_pCAXchgContextCurrent || NULL == g_pCAXchgContextCurrent->pccCA) { hr = pkcsLoadCAXchgContextArray(&fIncompleteLoad); _PrintIfError(hr, "pkcsLoadCAXchgContextArray"); if (S_OK != hr) { fNewCert = TRUE; } } if (NULL != g_pCAXchgContextCurrent && NULL != g_pCAXchgContextCurrent->pccCA) { CERT_INFO const *pCertInfo = g_pCAXchgContextCurrent->pccCA->pCertInfo; GetSystemTimeAsFileTime(&ft); if (0 < CompareFileTime(&ft, &pCertInfo->NotAfter)) { g_pCAXchgContextCurrent->Flags |= CTXF_EXPIRED; hr = CERT_E_EXPIRED; _PrintError(hr, "CA Xchg certificate is expired -- delete key"); pkcsDeleteKey(g_pCAXchgContextCurrent->pwszKeyContainerName); fNewCert = TRUE; } else if (0 > CompareFileTime(&ft, &pCertInfo->NotBefore)) { hr = CERT_E_EXPIRED; _PrintError(hr, "CA Xchg certificate not yet valid"); fNewCert = TRUE; } else { myMakeExprDateTime( &ft, g_lCAXchgOverlapPeriodCount, g_enumCAXchgOverlapPeriod); if (0 < CompareFileTime(&ft, &pCertInfo->NotAfter)) { hr = CERT_E_EXPIRED; _PrintError(hr, "CA Xchg certificate expires too soon"); fNewCert = TRUE; } else { hr = pkcsVerifyCertIssuer( g_pCAXchgContextCurrent->pccCA, g_pCAContextCurrent); if (S_OK != hr) { _PrintError(hr, "CA Xchg cert not issued by current CA"); fNewCert = TRUE; } else { LONG RevocationReason; LONG Disposition; hr = PKCSIsRevoked( g_pCAXchgContextCurrent->ReqId, NULL, // pwszSerialNumber &RevocationReason, &Disposition); if (S_OK != hr) { _PrintError(hr, "PKCSIsRevoked"); fNewCert = TRUE; } else if (CA_DISP_VALID != Disposition) { hr = CRYPT_E_REVOKED; _PrintError(hr, "revoked or bad CA Xchg certificate"); fNewCert = TRUE; } } } } } if (fNewCert) { hr = pkcsCreateNewCAXchgCert(pwszUserName); _JumpIfError(hr, error, "pkcsCreateNewCAXchgCert"); } hr = pkcsUpdateCAXchgStoreAndRegistry(fNewCert || fIncompleteLoad); _JumpIfError(hr, error, "pkcsUpdateCAXchgStoreAndRegistry"); *piCertSig = g_pCAXchgContextCurrent->iCertSig; *pcbCACert = g_pCAXchgContextCurrent->pccCA->cbCertEncoded; *ppbCACert = g_pCAXchgContextCurrent->pccCA->pbCertEncoded; hr = S_OK; error: return(hr); } HRESULT PKCSGetCAXchgChain( IN DWORD iCert, IN WCHAR const *pwszUserName, IN BOOL fIncludeCRLs, OUT BYTE **ppbCAChain, // CoTaskMem* OUT DWORD *pcbCAChain) { HRESULT hr; BYTE *pbCACert; DWORD cbCACert; CACTX *pCAContext; hr = PKCSGetCAXchgCert(iCert, pwszUserName, &iCert, &pbCACert, &cbCACert); _JumpIfError(hr, error, "PKCSGetCAXchgCert"); // iCert now indexes the signature cert that signed the current Xchg cert pCAContext = &g_aCAContext[iCert]; if (NULL == pCAContext->pccCA) { hr = E_INVALIDARG; _JumpError(hr, error, "invalid cert"); } hr = pkcsEncodeCertChain( pCAContext, pbCACert, // pbCertLeaf cbCACert, // cbCertLeaf pbCACert, // pbToBeSigned cbCACert, // cbToBeSigned fIncludeCRLs, ppbCAChain, // CoTaskMem* pcbCAChain); _JumpIfError(hr, error, "PKCSEncodeCertChain"); error: return(hr); } VOID PKCSTerminate(VOID) { pkcsReleaseCAContextArray(); pkcsReleaseCAXchgContextArray(); if (NULL != g_hStoreCAXchg) { CertCloseStore(g_hStoreCAXchg, CERT_CLOSE_STORE_CHECK_FLAG); g_hStoreCAXchg = NULL; } pkcsLoadCAXchgCSPInfo(TRUE); pkcsReleaseKRACertArray(); pkcsFreeTemplates(&g_paRevURL, &g_caRevURL); pkcsFreeTemplates(&g_paCACertURL, &g_caCACertURL); if (NULL != g_pwszKRAPublishURL) { LocalFree(g_pwszKRAPublishURL); g_pwszKRAPublishURL = NULL; } if (NULL != g_pwszAIACrossCertPublishURL) { LocalFree(g_pwszAIACrossCertPublishURL); g_pwszAIACrossCertPublishURL = NULL; } if (NULL != g_pwszRootTrustCrossCertPublishURL) { LocalFree(g_pwszRootTrustCrossCertPublishURL); g_pwszRootTrustCrossCertPublishURL = NULL; } if (NULL != g_strDomainDN) { SysFreeString(g_strDomainDN); g_strDomainDN = NULL; } if (NULL != g_strConfigDN) { SysFreeString(g_strConfigDN); g_strConfigDN = NULL; } } // PKCSCreateCertificate -- Create certificate & build PKCS 7 or Full Response. // // If pResult->pctbCert is non-NULL and pResult->pctbCert->pb is NULL: // CR_IN_NEW: // Build, store and return cert // Use current CA Context // Build and return PKCS 7 or Full Response // // If pResult->pctbCert is non-NULL and pResult->pctbCert->pb is non-NULL: // CR_IN_RETRIEVEPENDING: // Use passed cert // Find matching CA Context // Build and return PKCS 7 or Full Response // // If pResult->pctbCert is NULL: // CR_IN_RESUBMIT: // Build and store cert -- don't return cert // Use current CA Context // Don't build or return PKCS 7 or Full Response HRESULT PKCSCreateCertificate( IN ICertDBRow *prow, IN DWORD Disposition, IN BOOL fIncludeCRLs, OUT BOOL *pfErrorLogged, OPTIONAL OUT CACTX **ppCAContext, IN OUT CERTSRV_RESULT_CONTEXT *pResult) // CoTaskMem* { HRESULT hr; BYTE *pbCert = NULL; DWORD cbCert; BYTE *pbCertChain = NULL; DWORD cbCertChain; DWORD cCert; BOOL fCreated = FALSE; DWORD i; CACTX *pCAContext; CERT_CONTEXT const *pcc = NULL; if (NULL != ppCAContext) { *ppCAContext = NULL; } *pfErrorLogged = FALSE; CSASSERT(NULL == pResult->pctbCertChain || NULL == pResult->pctbCertChain->pb); CSASSERT(NULL == pResult->pctbFullResponse || NULL == pResult->pctbFullResponse->pb); if (NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb) { pbCert = pResult->pctbCert->pb; cbCert = pResult->pctbCert->cb; pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert); if (NULL == pcc) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } pCAContext = NULL; if (DB_DISP_CA_CERT != Disposition && DB_DISP_CA_CERT_CHAIN != Disposition) { hr = PKCSVerifyIssuedCertificate(pcc, &pCAContext); _JumpIfError(hr, error, "PKCSVerifyIssuedCertificate"); } } else { pCAContext = g_pCAContextCurrent; cbCert = 0; hr = pkcsEncodeSubjectCert( prow, pCAContext, &pbCert, // CoTaskMem* &cbCert, pfErrorLogged); _JumpIfError(hr, error, "pkcsEncodeSubjectCert"); fCreated = TRUE; hr = prow->SetProperty( g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, cbCert, pbCert); _JumpIfError(hr, error, "SetProperty"); } if (NULL != pResult->pctbCertChain) { hr = pkcsEncodeCertChain( pCAContext, pbCert, // pbCertLeaf cbCert, // cbCertLeaf pbCert, // pbToBeSigned cbCert, // cbToBeSigned fIncludeCRLs, &pbCertChain, // CoTaskMem* &cbCertChain); _JumpIfError(hr, error, "pkcsEncodeCertChain"); } if (fCreated && NULL != pResult->pctbCert) { pResult->pctbCert->pb = pbCert; pResult->pctbCert->cb = cbCert; pbCert = NULL; } if (NULL != pResult->pctbCertChain) { pResult->pctbCertChain->pb = pbCertChain; pResult->pctbCertChain->cb = cbCertChain; pbCertChain = NULL; } if (NULL != ppCAContext) { *ppCAContext = pCAContext; } hr = S_OK; error: if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (fCreated && NULL != pbCert) { CoTaskMemFree(pbCert); } if (fCreated && NULL != pbCertChain) { CoTaskMemFree(pbCertChain); } CSASSERT( NULL == pResult->pctbCertChain || ((S_OK == hr) ^ (NULL == pResult->pctbCertChain->pb))); return(hr); } HRESULT PKCSGetKRACert( IN DWORD iCert, OUT BYTE **ppbCert, OUT DWORD *pcbCert) { HRESULT hr = S_OK; DWORD State; if (MAXDWORD == iCert) { iCert = g_iKRACerts; } if (iCert >= g_cKRACerts) { hr = E_INVALIDARG; _JumpError(hr, error, "bad CertIndex"); } *pcbCert = g_rgKRACerts[iCert]->cbCertEncoded; *ppbCert = g_rgKRACerts[iCert]->pbCertEncoded; error: return(hr); }