|
|
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: pkcs.cpp
//
// Contents: Cert Server Extension interfaces -- PKCS implementation
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <stdio.h>
#include <time.h>
#define SECURITY_WIN32
#include <security.h>
#include "resource.h"
#include "cscom.h"
#include "csprop.h"
#include "elog.h"
#include "certlog.h"
#include "csdisp.h"
#include "cscsp.h"
#include <certca.h>
#include <esent.h>
#include <winldap.h>
#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<CERT_NAME_BLOB *>(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<CERT_PUBLIC_KEY_INFO *>(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<BYTE *>(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<BYTE *>(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<CERT_PUBLIC_KEY_INFO *>( &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<WCHAR *>(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(dwDefaultBufSize<dwBufLen) { WCHAR *pTempBuf = (WCHAR*)LocalReAlloc( wszBuf, dwBufLen, LMEM_MOVEABLE); if(NULL == pTempBuf) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalReAlloc"); } wszBuf = pTempBuf; }
wcscat(wszBuf, L" ("); wcscat(wszBuf, pwszObjectIdDescription); wcscat(wszBuf, L")"); } apwsz[cString++] = wszBuf;
hr = CERTSRV_E_ENCODING_LENGTH; wsprintf(wszDword, L"0x%x", hr);
if (CERTLOG_ERROR <= g_dwLogLevel) { LogEvent( EVENTLOG_ERROR_TYPE, MSG_E_BADCERTLENGTHFIELD, cString, apwsz); }
CONSOLEPRINT4(( DBG_SS_CERTSRV, "CertSrv Request %u: rc=%x: Bad encoded length detected: %ws \"%ws\"\n", ReqId, hr, wszBuf, awcSubject)); *pfErrorLogged = TRUE; } error:
if (NULL != wszBuf) { LocalFree(wszBuf); } return(hr); }
HRESULT pkcsCreateCertSerialNumber( IN ICertDBRow *prow, IN CACTX const *pCAContext, OUT BSTR *pstrSerialNumber) { HRESULT hr; DWORD dw; USHORT us; BYTE abRandom[8]; BYTE abSerial[max( sizeof(dw) + sizeof(us) + sizeof(dw), sizeof(dw) + sizeof(us) + sizeof(abRandom) + sizeof(dw) + sizeof(BYTE))]; BSTR strSerialNumber = NULL; DWORD cbSerial; BYTE *pb; DWORD cb; //#define TEST_SPECIAL_SERIAL_NUMBERS
#ifdef TEST_SPECIAL_SERIAL_NUMBERS
BOOL fAddZeroByte = FALSE; #endif
*pstrSerialNumber = NULL; pb = abSerial;
prow->GetRowId(&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<BYTE *>(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); }
|