windows-nt/Source/XPSP1/NT/ds/security/services/ca/certsrv/pkcs.cpp
2020-09-26 16:20:57 +08:00

11800 lines
268 KiB
C++

//+--------------------------------------------------------------------------
//
// 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);
}